aboutsummaryrefslogtreecommitdiff
path: root/engines
diff options
context:
space:
mode:
authorPaul Gilbert2016-03-20 14:55:41 -0400
committerPaul Gilbert2016-03-20 14:55:41 -0400
commit3c852cc240221785598023de56e5a71a0d8806fa (patch)
treec2a64b811586e4e9d8a4d5baa04d5c4f9d23bd08 /engines
parentea54e6244e75c609e6886ba210f80fb22c479d3f (diff)
parent509a00109e79156e91c062f145ac3aa86ec8584e (diff)
downloadscummvm-rg350-3c852cc240221785598023de56e5a71a0d8806fa.tar.gz
scummvm-rg350-3c852cc240221785598023de56e5a71a0d8806fa.tar.bz2
scummvm-rg350-3c852cc240221785598023de56e5a71a0d8806fa.zip
Merge branch 'master' into titanic
Diffstat (limited to 'engines')
-rw-r--r--engines/access/access.cpp17
-rw-r--r--engines/access/asurface.cpp196
-rw-r--r--engines/access/asurface.h24
-rw-r--r--engines/access/detection.cpp3
-rw-r--r--engines/access/detection_tables.h2
-rw-r--r--engines/access/events.cpp5
-rw-r--r--engines/access/events.h2
-rw-r--r--engines/access/files.cpp21
-rw-r--r--engines/access/files.h6
-rw-r--r--engines/access/font.cpp5
-rw-r--r--engines/access/screen.cpp87
-rw-r--r--engines/access/screen.h30
-rw-r--r--engines/access/scripts.cpp5
-rw-r--r--engines/access/video.cpp2
-rw-r--r--engines/access/video/movie_decoder.cpp2
-rw-r--r--engines/advancedDetector.cpp79
-rw-r--r--engines/advancedDetector.h15
-rw-r--r--engines/agi/agi.cpp158
-rw-r--r--engines/agi/agi.h68
-rw-r--r--engines/agi/appleIIgs_timedelay_overwrite.h91
-rw-r--r--engines/agi/checks.cpp2
-rw-r--r--engines/agi/console.cpp55
-rw-r--r--engines/agi/console.h1
-rw-r--r--engines/agi/cycle.cpp81
-rw-r--r--engines/agi/detection.cpp34
-rw-r--r--engines/agi/detection_tables.h20
-rw-r--r--engines/agi/font.cpp209
-rw-r--r--engines/agi/font.h3
-rw-r--r--engines/agi/global.cpp159
-rw-r--r--engines/agi/graphics.cpp779
-rw-r--r--engines/agi/graphics.h105
-rw-r--r--engines/agi/inv.cpp2
-rw-r--r--engines/agi/keyboard.cpp36
-rw-r--r--engines/agi/menu.cpp48
-rw-r--r--engines/agi/menu.h4
-rw-r--r--engines/agi/motion.cpp54
-rw-r--r--engines/agi/op_cmd.cpp115
-rw-r--r--engines/agi/opcodes.cpp10
-rw-r--r--engines/agi/palette.h19
-rw-r--r--engines/agi/picture.cpp17
-rw-r--r--engines/agi/preagi.cpp4
-rw-r--r--engines/agi/preagi_mickey.cpp2
-rw-r--r--engines/agi/saveload.cpp94
-rw-r--r--engines/agi/sound_2gs.cpp2
-rw-r--r--engines/agi/sprite.cpp27
-rw-r--r--engines/agi/systemui.cpp157
-rw-r--r--engines/agi/systemui.h9
-rw-r--r--engines/agi/text.cpp170
-rw-r--r--engines/agi/text.h6
-rw-r--r--engines/agi/view.cpp46
-rw-r--r--engines/agi/view.h6
-rw-r--r--engines/agi/words.cpp2
-rw-r--r--engines/agi/words.h2
-rw-r--r--engines/agos/configure.engine2
-rw-r--r--engines/agos/detection.cpp9
-rw-r--r--engines/agos/sound.cpp2
-rw-r--r--engines/avalanche/POTFILES1
-rw-r--r--engines/avalanche/configure.engine2
-rw-r--r--engines/avalanche/detection.cpp5
-rw-r--r--engines/avalanche/parser.cpp5
-rw-r--r--engines/bbvs/bbvs.cpp33
-rw-r--r--engines/bbvs/detection.cpp9
-rw-r--r--engines/bbvs/dialogs.cpp2
-rw-r--r--engines/cge/POTFILES1
-rw-r--r--engines/cge/detection.cpp5
-rw-r--r--engines/cge/events.cpp5
-rw-r--r--engines/cge2/POTFILES1
-rw-r--r--engines/cge2/detection.cpp5
-rw-r--r--engines/cge2/events.cpp5
-rw-r--r--engines/cge2/vga13h.cpp4
-rw-r--r--engines/cine/anim.cpp2
-rw-r--r--engines/cine/detection.cpp11
-rw-r--r--engines/cine/saveload.cpp5
-rw-r--r--engines/cine/script_fw.cpp4
-rw-r--r--engines/composer/configure.engine2
-rw-r--r--engines/composer/detection.cpp8
-rw-r--r--engines/cruise/detection.cpp9
-rw-r--r--engines/draci/detection.cpp5
-rw-r--r--engines/drascula/detection.cpp7
-rw-r--r--engines/dreamweb/detection.cpp7
-rw-r--r--engines/engine.cpp2
-rw-r--r--engines/fullpipe/configure.engine2
-rw-r--r--engines/fullpipe/detection.cpp7
-rw-r--r--engines/game.cpp6
-rw-r--r--engines/game.h4
-rw-r--r--engines/gob/detection/detection.cpp10
-rw-r--r--engines/gob/script.cpp2
-rw-r--r--engines/gob/sound/adlib.cpp7
-rw-r--r--engines/groovie/configure.engine2
-rw-r--r--engines/groovie/detection.cpp4
-rw-r--r--engines/groovie/groovie.cpp2
-rw-r--r--engines/hopkins/configure.engine2
-rw-r--r--engines/hopkins/detection.cpp3
-rw-r--r--engines/hugo/POTFILES1
-rw-r--r--engines/hugo/detection.cpp5
-rw-r--r--engines/hugo/file.cpp5
-rw-r--r--engines/kyra/debugger.cpp4
-rw-r--r--engines/kyra/detection.cpp3
-rw-r--r--engines/kyra/detection_tables.h16
-rw-r--r--engines/kyra/gui_lol.cpp2
-rw-r--r--engines/kyra/gui_v1.cpp2
-rw-r--r--engines/kyra/sound.cpp18
-rw-r--r--engines/lab/configure.engine2
-rw-r--r--engines/lab/detection.cpp32
-rw-r--r--engines/lab/lab.cpp10
-rw-r--r--engines/lab/processroom.cpp6
-rw-r--r--engines/lab/savegame.cpp21
-rw-r--r--engines/lastexpress/configure.engine2
-rw-r--r--engines/lastexpress/detection.cpp4
-rw-r--r--engines/lure/detection.cpp7
-rw-r--r--engines/made/detection.cpp2
-rw-r--r--engines/mads/debugger.cpp2
-rw-r--r--engines/mads/detection.cpp3
-rw-r--r--engines/mads/dialogs.cpp50
-rw-r--r--engines/mads/dragonsphere/dragonsphere_scenes.cpp2
-rw-r--r--engines/mads/events.cpp6
-rw-r--r--engines/mads/font.cpp21
-rw-r--r--engines/mads/game.cpp5
-rw-r--r--engines/mads/mads.cpp13
-rw-r--r--engines/mads/mads.h2
-rw-r--r--engines/mads/menu_views.cpp30
-rw-r--r--engines/mads/msurface.cpp343
-rw-r--r--engines/mads/msurface.h144
-rw-r--r--engines/mads/nebular/dialogs_nebular.cpp32
-rw-r--r--engines/mads/nebular/game_nebular.cpp76
-rw-r--r--engines/mads/nebular/menu_nebular.cpp4
-rw-r--r--engines/mads/nebular/nebular_scenes.cpp4
-rw-r--r--engines/mads/nebular/nebular_scenes3.cpp18
-rw-r--r--engines/mads/nebular/nebular_scenes5.cpp2
-rw-r--r--engines/mads/phantom/phantom_scenes.cpp2
-rw-r--r--engines/mads/rails.cpp2
-rw-r--r--engines/mads/scene.cpp9
-rw-r--r--engines/mads/scene_data.cpp13
-rw-r--r--engines/mads/screen.cpp106
-rw-r--r--engines/mads/screen.h36
-rw-r--r--engines/mads/sequence.cpp4
-rw-r--r--engines/mads/sprites.cpp26
-rw-r--r--engines/mads/user_interface.cpp46
-rw-r--r--engines/mads/user_interface.h2
-rw-r--r--engines/metaengine.h3
-rw-r--r--engines/mohawk/configure.engine4
-rw-r--r--engines/mohawk/console.cpp42
-rw-r--r--engines/mohawk/console.h1
-rw-r--r--engines/mohawk/detection.cpp62
-rw-r--r--engines/mohawk/detection_tables.h41
-rw-r--r--engines/mohawk/dialogs.cpp92
-rw-r--r--engines/mohawk/dialogs.h18
-rw-r--r--engines/mohawk/myst.cpp581
-rw-r--r--engines/mohawk/myst.h124
-rw-r--r--engines/mohawk/myst_areas.cpp411
-rw-r--r--engines/mohawk/myst_areas.h162
-rw-r--r--engines/mohawk/myst_graphics.cpp24
-rw-r--r--engines/mohawk/myst_graphics.h4
-rw-r--r--engines/mohawk/myst_scripts.cpp129
-rw-r--r--engines/mohawk/myst_scripts.h28
-rw-r--r--engines/mohawk/myst_stacks/channelwood.cpp59
-rw-r--r--engines/mohawk/myst_stacks/channelwood.h14
-rw-r--r--engines/mohawk/myst_stacks/credits.cpp5
-rw-r--r--engines/mohawk/myst_stacks/credits.h6
-rw-r--r--engines/mohawk/myst_stacks/demo.h4
-rw-r--r--engines/mohawk/myst_stacks/dni.h6
-rw-r--r--engines/mohawk/myst_stacks/intro.cpp2
-rw-r--r--engines/mohawk/myst_stacks/intro.h10
-rw-r--r--engines/mohawk/myst_stacks/makingof.h4
-rw-r--r--engines/mohawk/myst_stacks/mechanical.cpp94
-rw-r--r--engines/mohawk/myst_stacks/mechanical.h26
-rw-r--r--engines/mohawk/myst_stacks/myst.cpp191
-rw-r--r--engines/mohawk/myst_stacks/myst.h56
-rw-r--r--engines/mohawk/myst_stacks/preview.cpp6
-rw-r--r--engines/mohawk/myst_stacks/preview.h8
-rw-r--r--engines/mohawk/myst_stacks/selenitic.cpp98
-rw-r--r--engines/mohawk/myst_stacks/selenitic.h61
-rw-r--r--engines/mohawk/myst_stacks/slides.h4
-rw-r--r--engines/mohawk/myst_stacks/stoneship.cpp38
-rw-r--r--engines/mohawk/myst_stacks/stoneship.h20
-rw-r--r--engines/mohawk/myst_state.cpp209
-rw-r--r--engines/mohawk/myst_state.h37
-rw-r--r--engines/mohawk/sound.cpp33
-rw-r--r--engines/mohawk/sound.h2
-rw-r--r--engines/mohawk/video.cpp135
-rw-r--r--engines/mohawk/video.h4
-rw-r--r--engines/mortevielle/configure.engine2
-rw-r--r--engines/mortevielle/detection.cpp2
-rw-r--r--engines/neverhood/configure.engine2
-rw-r--r--engines/neverhood/detection.cpp9
-rw-r--r--engines/parallaction/detection.cpp5
-rw-r--r--engines/parallaction/saveload.cpp4
-rw-r--r--engines/pegasus/configure.engine2
-rw-r--r--engines/pegasus/detection.cpp2
-rw-r--r--engines/prince/configure.engine2
-rw-r--r--engines/prince/detection.cpp2
-rw-r--r--engines/prince/detection.h2
-rw-r--r--engines/prince/graphics.cpp2
-rw-r--r--engines/queen/detection.cpp30
-rw-r--r--engines/saga/configure.engine4
-rw-r--r--engines/saga/detection.cpp9
-rw-r--r--engines/saga/interface.cpp9
-rw-r--r--engines/saga/music.cpp3
-rw-r--r--engines/saga/puzzle.cpp3
-rw-r--r--engines/saga/render.cpp3
-rw-r--r--engines/saga/saga.cpp8
-rw-r--r--engines/savestate.h9
-rw-r--r--engines/sci/configure.engine2
-rw-r--r--engines/sci/console.cpp112
-rw-r--r--engines/sci/console.h3
-rw-r--r--engines/sci/decompressor.cpp2
-rw-r--r--engines/sci/detection.cpp27
-rw-r--r--engines/sci/detection_tables.h124
-rw-r--r--engines/sci/engine/kernel.cpp2
-rw-r--r--engines/sci/engine/kernel.h53
-rw-r--r--engines/sci/engine/kernel_tables.h157
-rw-r--r--engines/sci/engine/kevent.cpp27
-rw-r--r--engines/sci/engine/kfile.cpp57
-rw-r--r--engines/sci/engine/kgraphics.cpp47
-rw-r--r--engines/sci/engine/kgraphics32.cpp931
-rw-r--r--engines/sci/engine/kmisc.cpp19
-rw-r--r--engines/sci/engine/kpathing.cpp12
-rw-r--r--engines/sci/engine/ksound.cpp42
-rw-r--r--engines/sci/engine/kstring.cpp20
-rw-r--r--engines/sci/engine/object.cpp9
-rw-r--r--engines/sci/engine/object.h39
-rw-r--r--engines/sci/engine/savegame.cpp111
-rw-r--r--engines/sci/engine/savegame.h3
-rw-r--r--engines/sci/engine/script_patches.cpp674
-rw-r--r--engines/sci/engine/script_patches.h21
-rw-r--r--engines/sci/engine/scriptdebug.cpp4
-rw-r--r--engines/sci/engine/seg_manager.cpp9
-rw-r--r--engines/sci/engine/segment.h2
-rw-r--r--engines/sci/engine/selector.cpp29
-rw-r--r--engines/sci/engine/selector.h18
-rw-r--r--engines/sci/engine/state.cpp1
-rw-r--r--engines/sci/engine/state.h2
-rw-r--r--engines/sci/engine/vm.cpp14
-rw-r--r--engines/sci/engine/vm_types.cpp38
-rw-r--r--engines/sci/engine/vm_types.h17
-rw-r--r--engines/sci/engine/workarounds.cpp11
-rw-r--r--engines/sci/engine/workarounds.h1
-rw-r--r--engines/sci/event.cpp56
-rw-r--r--engines/sci/event.h18
-rw-r--r--engines/sci/graphics/animate.cpp92
-rw-r--r--engines/sci/graphics/animate.h10
-rw-r--r--engines/sci/graphics/cache.cpp4
-rw-r--r--engines/sci/graphics/cache.h2
-rw-r--r--engines/sci/graphics/celobj32.cpp1050
-rw-r--r--engines/sci/graphics/celobj32.h581
-rw-r--r--engines/sci/graphics/compare.cpp74
-rw-r--r--engines/sci/graphics/compare.h3
-rw-r--r--engines/sci/graphics/controls16.cpp2
-rw-r--r--engines/sci/graphics/controls32.cpp426
-rw-r--r--engines/sci/graphics/controls32.h83
-rw-r--r--engines/sci/graphics/cursor.cpp12
-rw-r--r--engines/sci/graphics/cursor.h12
-rw-r--r--engines/sci/graphics/frameout.cpp2005
-rw-r--r--engines/sci/graphics/frameout.h504
-rw-r--r--engines/sci/graphics/helpers.h106
-rw-r--r--engines/sci/graphics/lists32.h192
-rw-r--r--engines/sci/graphics/palette.cpp90
-rw-r--r--engines/sci/graphics/palette.h21
-rw-r--r--engines/sci/graphics/palette32.cpp108
-rw-r--r--engines/sci/graphics/palette32.h392
-rw-r--r--engines/sci/graphics/picture.cpp6
-rw-r--r--engines/sci/graphics/picture.h3
-rw-r--r--engines/sci/graphics/plane32.cpp907
-rw-r--r--engines/sci/graphics/plane32.h485
-rw-r--r--engines/sci/graphics/remap.cpp386
-rw-r--r--engines/sci/graphics/remap.h154
-rw-r--r--engines/sci/graphics/screen.cpp278
-rw-r--r--engines/sci/graphics/screen.h315
-rw-r--r--engines/sci/graphics/screen_item32.cpp647
-rw-r--r--engines/sci/graphics/screen_item32.h288
-rw-r--r--engines/sci/graphics/text32.cpp822
-rw-r--r--engines/sci/graphics/text32.h457
-rw-r--r--engines/sci/graphics/view.cpp45
-rw-r--r--engines/sci/graphics/view.h2
-rw-r--r--engines/sci/module.mk4
-rw-r--r--engines/sci/parser/vocabulary.cpp14
-rw-r--r--engines/sci/parser/vocabulary.h2
-rw-r--r--engines/sci/resource.cpp75
-rw-r--r--engines/sci/resource.h9
-rw-r--r--engines/sci/resource_audio.cpp8
-rw-r--r--engines/sci/sci.cpp76
-rw-r--r--engines/sci/sci.h13
-rw-r--r--engines/sci/sound/drivers/amigamac.cpp2
-rw-r--r--engines/sci/sound/soundcmd.cpp2
-rw-r--r--engines/sci/util.cpp9
-rw-r--r--engines/sci/util.h3
-rw-r--r--engines/scumm/actor.cpp2
-rw-r--r--engines/scumm/charset-fontdata.cpp31
-rw-r--r--engines/scumm/configure.engine2
-rw-r--r--engines/scumm/detection.cpp3
-rw-r--r--engines/scumm/he/sound_he.cpp4
-rw-r--r--engines/scumm/imuse_digi/dimuse_track.cpp4
-rw-r--r--engines/scumm/players/player_ad.cpp30
-rw-r--r--engines/scumm/players/player_ad.h2
-rw-r--r--engines/scumm/players/player_v4a.cpp4
-rw-r--r--engines/scumm/saveload.cpp2
-rw-r--r--engines/scumm/scumm.cpp8
-rw-r--r--engines/scumm/scumm_v4.h2
-rw-r--r--engines/scumm/sound.cpp2
-rw-r--r--engines/sherlock/POTFILES3
-rw-r--r--engines/sherlock/animation.cpp6
-rw-r--r--engines/sherlock/events.cpp8
-rw-r--r--engines/sherlock/fixed_text.cpp184
-rw-r--r--engines/sherlock/fixed_text.h47
-rw-r--r--engines/sherlock/fonts.cpp4
-rw-r--r--engines/sherlock/fonts.h2
-rw-r--r--engines/sherlock/image_file.h5
-rw-r--r--engines/sherlock/journal.cpp154
-rw-r--r--engines/sherlock/module.mk2
-rw-r--r--engines/sherlock/objects.cpp26
-rw-r--r--engines/sherlock/scalpel/3do/scalpel_3do_screen.cpp286
-rw-r--r--engines/sherlock/scalpel/3do/scalpel_3do_screen.h76
-rw-r--r--engines/sherlock/scalpel/scalpel.cpp107
-rw-r--r--engines/sherlock/scalpel/scalpel_darts.cpp36
-rw-r--r--engines/sherlock/scalpel/scalpel_fixed_text.cpp29
-rw-r--r--engines/sherlock/scalpel/scalpel_fixed_text.h8
-rw-r--r--engines/sherlock/scalpel/scalpel_inventory.cpp8
-rw-r--r--engines/sherlock/scalpel/scalpel_map.cpp43
-rw-r--r--engines/sherlock/scalpel/scalpel_scene.cpp44
-rw-r--r--engines/sherlock/scalpel/scalpel_screen.cpp251
-rw-r--r--engines/sherlock/scalpel/scalpel_screen.h38
-rw-r--r--engines/sherlock/scalpel/scalpel_user_interface.cpp165
-rw-r--r--engines/sherlock/scalpel/tsage/logo.cpp8
-rw-r--r--engines/sherlock/scene.cpp11
-rw-r--r--engines/sherlock/screen.cpp121
-rw-r--r--engines/sherlock/screen.h48
-rw-r--r--engines/sherlock/sherlock.h6
-rw-r--r--engines/sherlock/sound.cpp4
-rw-r--r--engines/sherlock/surface.cpp260
-rw-r--r--engines/sherlock/surface.h152
-rw-r--r--engines/sherlock/tattoo/tattoo_darts.cpp165
-rw-r--r--engines/sherlock/tattoo/tattoo_fixed_text.cpp105
-rw-r--r--engines/sherlock/tattoo/tattoo_fixed_text.h40
-rw-r--r--engines/sherlock/tattoo/tattoo_journal.cpp222
-rw-r--r--engines/sherlock/tattoo/tattoo_journal.h12
-rw-r--r--engines/sherlock/tattoo/tattoo_map.cpp30
-rw-r--r--engines/sherlock/tattoo/tattoo_people.cpp7
-rw-r--r--engines/sherlock/tattoo/tattoo_scene.cpp22
-rw-r--r--engines/sherlock/tattoo/tattoo_screen.cpp37
-rw-r--r--engines/sherlock/tattoo/tattoo_screen.h44
-rw-r--r--engines/sherlock/tattoo/tattoo_talk.cpp5
-rw-r--r--engines/sherlock/tattoo/tattoo_user_interface.cpp16
-rw-r--r--engines/sherlock/tattoo/widget_base.cpp40
-rw-r--r--engines/sherlock/tattoo/widget_credits.cpp17
-rw-r--r--engines/sherlock/tattoo/widget_files.cpp28
-rw-r--r--engines/sherlock/tattoo/widget_foolscap.cpp4
-rw-r--r--engines/sherlock/tattoo/widget_inventory.cpp22
-rw-r--r--engines/sherlock/tattoo/widget_options.cpp32
-rw-r--r--engines/sherlock/tattoo/widget_password.cpp6
-rw-r--r--engines/sherlock/tattoo/widget_quit.cpp20
-rw-r--r--engines/sherlock/tattoo/widget_talk.cpp4
-rw-r--r--engines/sherlock/tattoo/widget_text.cpp5
-rw-r--r--engines/sherlock/tattoo/widget_tooltip.cpp4
-rw-r--r--engines/sherlock/tattoo/widget_verbs.cpp6
-rw-r--r--engines/sky/detection.cpp7
-rw-r--r--engines/sword1/configure.engine2
-rw-r--r--engines/sword1/detection.cpp15
-rw-r--r--engines/sword1/objectman.cpp6
-rw-r--r--engines/sword1/objectman.h2
-rw-r--r--engines/sword1/router.cpp2
-rw-r--r--engines/sword2/configure.engine2
-rw-r--r--engines/sword2/router.cpp2
-rw-r--r--engines/sword2/sword2.cpp3
-rw-r--r--engines/sword25/POTFILES1
-rw-r--r--engines/sword25/configure.engine2
-rw-r--r--engines/sword25/detection.cpp17
-rw-r--r--engines/sword25/detection_tables.h29
-rw-r--r--engines/sword25/fmv/movieplayer.cpp2
-rw-r--r--engines/sword25/fmv/movieplayer.h2
-rw-r--r--engines/sword25/gfx/animationresource.cpp5
-rw-r--r--engines/sword25/gfx/fontresource.cpp5
-rw-r--r--engines/sword25/gfx/text.cpp3
-rw-r--r--engines/sword25/kernel/kernel_script.cpp1
-rw-r--r--engines/sword25/package/packagemanager.cpp45
-rw-r--r--engines/sword25/package/packagemanager.h3
-rw-r--r--engines/sword25/script/luacallback.cpp2
-rw-r--r--engines/sword25/script/luascript.cpp2
-rw-r--r--engines/sword25/sword25.cpp2
-rw-r--r--engines/sword25/util/lua/lbaselib.cpp2
-rw-r--r--engines/sword25/util/lua/ldo.cpp7
-rw-r--r--engines/sword25/util/lua/luaconf.h2
-rw-r--r--engines/teenagent/detection.cpp5
-rw-r--r--engines/testbed/detection.cpp2
-rw-r--r--engines/tinsel/detection.cpp13
-rw-r--r--engines/tinsel/graphics.cpp2
-rw-r--r--engines/tinsel/music.cpp20
-rw-r--r--engines/tinsel/tinsel.cpp2
-rw-r--r--engines/toltecs/configure.engine2
-rw-r--r--engines/toltecs/detection.cpp5
-rw-r--r--engines/tony/configure.engine2
-rw-r--r--engines/tony/detection.cpp3
-rw-r--r--engines/toon/POTFILES1
-rw-r--r--engines/toon/configure.engine2
-rw-r--r--engines/toon/detection.cpp5
-rw-r--r--engines/toon/toon.cpp5
-rw-r--r--engines/touche/configure.engine2
-rw-r--r--engines/touche/detection.cpp2
-rw-r--r--engines/tsage/blue_force/blueforce_dialogs.cpp4
-rw-r--r--engines/tsage/blue_force/blueforce_logic.cpp4
-rw-r--r--engines/tsage/blue_force/blueforce_scenes6.cpp4
-rw-r--r--engines/tsage/converse.cpp2
-rw-r--r--engines/tsage/core.cpp10
-rw-r--r--engines/tsage/detection.cpp7
-rw-r--r--engines/tsage/detection_tables.h4
-rw-r--r--engines/tsage/events.cpp4
-rw-r--r--engines/tsage/globals.cpp4
-rw-r--r--engines/tsage/globals.h3
-rw-r--r--engines/tsage/graphics.cpp292
-rw-r--r--engines/tsage/graphics.h30
-rw-r--r--engines/tsage/module.mk1
-rw-r--r--engines/tsage/ringworld/ringworld_dialogs.cpp6
-rw-r--r--engines/tsage/ringworld/ringworld_logic.cpp2
-rw-r--r--engines/tsage/ringworld/ringworld_scenes3.cpp6
-rw-r--r--engines/tsage/ringworld/ringworld_scenes5.cpp4
-rw-r--r--engines/tsage/ringworld2/ringworld2_dialogs.cpp2
-rw-r--r--engines/tsage/ringworld2/ringworld2_logic.cpp35
-rw-r--r--engines/tsage/ringworld2/ringworld2_outpost.cpp2
-rw-r--r--engines/tsage/ringworld2/ringworld2_scenes0.cpp6
-rw-r--r--engines/tsage/saveload.cpp4
-rw-r--r--engines/tsage/scenes.cpp6
-rw-r--r--engines/tsage/screen.cpp46
-rw-r--r--engines/tsage/screen.h59
-rw-r--r--engines/tsage/tsage.h6
-rw-r--r--engines/tsage/user_interface.cpp12
-rw-r--r--engines/tucker/detection.cpp4
-rw-r--r--engines/tucker/resource.cpp8
-rw-r--r--engines/tucker/sequences.cpp23
-rw-r--r--engines/tucker/tucker.cpp4
-rw-r--r--engines/tucker/tucker.h1
-rw-r--r--engines/voyeur/animation.cpp7
-rw-r--r--engines/voyeur/data.cpp16
-rw-r--r--engines/voyeur/debugger.cpp2
-rw-r--r--engines/voyeur/detection.cpp3
-rw-r--r--engines/voyeur/events.cpp58
-rw-r--r--engines/voyeur/files.cpp115
-rw-r--r--engines/voyeur/files.h6
-rw-r--r--engines/voyeur/files_threads.cpp74
-rw-r--r--engines/voyeur/module.mk2
-rw-r--r--engines/voyeur/screen.cpp (renamed from engines/voyeur/graphics.cpp)127
-rw-r--r--engines/voyeur/screen.h (renamed from engines/voyeur/graphics.h)22
-rw-r--r--engines/voyeur/voyeur.cpp165
-rw-r--r--engines/voyeur/voyeur.h4
-rw-r--r--engines/voyeur/voyeur_game.cpp306
-rw-r--r--engines/wage/combat.cpp916
-rw-r--r--engines/wage/configure.engine3
-rw-r--r--engines/wage/debugger.cpp97
-rw-r--r--engines/wage/debugger.h47
-rw-r--r--engines/wage/design.cpp599
-rw-r--r--engines/wage/design.h119
-rw-r--r--engines/wage/detection.cpp151
-rw-r--r--engines/wage/detection_tables.h121
-rw-r--r--engines/wage/dialog.cpp241
-rw-r--r--engines/wage/dialog.h101
-rw-r--r--engines/wage/entities.cpp514
-rw-r--r--engines/wage/entities.h336
-rw-r--r--engines/wage/gui-console.cpp430
-rw-r--r--engines/wage/gui.cpp675
-rw-r--r--engines/wage/gui.h189
-rw-r--r--engines/wage/menu.cpp571
-rw-r--r--engines/wage/menu.h139
-rw-r--r--engines/wage/module.mk29
-rw-r--r--engines/wage/randomhat.cpp83
-rw-r--r--engines/wage/randomhat.h77
-rw-r--r--engines/wage/script.cpp1175
-rw-r--r--engines/wage/script.h161
-rw-r--r--engines/wage/sound.cpp86
-rw-r--r--engines/wage/sound.h64
-rw-r--r--engines/wage/util.cpp125
-rw-r--r--engines/wage/wage.cpp515
-rw-r--r--engines/wage/wage.h239
-rw-r--r--engines/wage/world.cpp523
-rw-r--r--engines/wage/world.h138
-rw-r--r--engines/wintermute/ad/ad_actor.cpp4
-rw-r--r--engines/wintermute/base/base_engine.h2
-rw-r--r--engines/wintermute/base/base_persistence_manager.cpp12
-rw-r--r--engines/wintermute/base/base_persistence_manager.h4
-rw-r--r--engines/wintermute/base/base_sub_frame.cpp2
-rw-r--r--engines/wintermute/base/file/base_disk_file.cpp27
-rw-r--r--engines/wintermute/configure.engine2
-rw-r--r--engines/wintermute/detection.cpp10
-rw-r--r--engines/wintermute/detection_tables.h5
-rw-r--r--engines/wintermute/wintermute.cpp2
-rw-r--r--engines/zvision/configure.engine2
-rw-r--r--engines/zvision/detection.cpp26
484 files changed, 26382 insertions, 7988 deletions
diff --git a/engines/access/access.cpp b/engines/access/access.cpp
index bc9bcb4b08..c12761af4a 100644
--- a/engines/access/access.cpp
+++ b/engines/access/access.cpp
@@ -436,20 +436,9 @@ void AccessEngine::copyBF1BF2() {
}
void AccessEngine::copyBF2Vid() {
- const byte *srcP = (const byte *)_buffer2.getPixels();
- byte *destP = (byte *)_screen->getBasePtr(_screen->_windowXAdd,
- _screen->_windowYAdd + _screen->_screenYOff);
-
- for (int yp = 0; yp < _screen->_vWindowLinesTall; ++yp) {
- Common::copy(srcP, srcP + _screen->_vWindowBytesWide, destP);
- srcP += _buffer2.pitch;
- destP += _screen->pitch;
- }
-
- // Add dirty rect for affected area
- Common::Rect r(_screen->_vWindowBytesWide, _screen->_vWindowLinesTall);
- r.moveTo(_screen->_windowXAdd, _screen->_windowYAdd + _screen->_screenYOff);
- _screen->addDirtyRect(r);
+ _screen->blitFrom(_buffer2,
+ Common::Rect(0, 0, _screen->_vWindowBytesWide, _screen->_vWindowLinesTall),
+ Common::Point(_screen->_windowXAdd, _screen->_windowYAdd));
}
void AccessEngine::playVideo(int videoNum, const Common::Point &pt) {
diff --git a/engines/access/asurface.cpp b/engines/access/asurface.cpp
index f693e6a3a0..2518ff6ad8 100644
--- a/engines/access/asurface.cpp
+++ b/engines/access/asurface.cpp
@@ -110,7 +110,7 @@ void ImageEntryList::addToList(ImageEntry &ie) {
int ASurface::_clipWidth;
int ASurface::_clipHeight;
-ASurface::ASurface(): Graphics::Surface() {
+ASurface::ASurface(): Graphics::ManagedSurface() {
_leftSkip = _rightSkip = 0;
_topSkip = _bottomSkip = 0;
_lastBoundsX = _lastBoundsY = 0;
@@ -122,65 +122,14 @@ ASurface::ASurface(): Graphics::Surface() {
}
ASurface::~ASurface() {
- free();
_savedBlock.free();
}
-void ASurface::create(uint16 width, uint16 height) {
- Graphics::Surface::create(width, height, Graphics::PixelFormat::createFormatCLUT8());
-}
-
void ASurface::clearBuffer() {
byte *pSrc = (byte *)getPixels();
Common::fill(pSrc, pSrc + w * h, 0);
}
-bool ASurface::clip(Common::Rect &r) {
- int skip;
- _leftSkip = _rightSkip = 0;
- _topSkip = _bottomSkip = 0;
-
- if (r.left > _clipWidth || r.left < 0) {
- if (r.left >= 0)
- return true;
-
- skip = -r.left;
- r.setWidth(r.width() - skip);
- _leftSkip = skip;
- r.moveTo(0, r.top);
- }
-
- int right = r.right - 1;
- if (right < 0)
- return true;
- else if (right > _clipWidth) {
- skip = right - _clipWidth;
- r.setWidth(r.width() - skip);
- _rightSkip = skip;
- }
-
- if (r.top > _clipHeight || r.top < 0) {
- if (r.top >= 0)
- return true;
-
- skip = -r.top;
- r.setHeight(r.height() - skip);
- _topSkip = skip;
- r.moveTo(r.left, 0);
- }
-
- int bottom = r.bottom - 1;
- if (bottom < 0)
- return true;
- else if (bottom > _clipHeight) {
- skip = bottom - _clipHeight;
- _bottomSkip = skip;
- r.setHeight(r.height() - skip);
- }
-
- return false;
-}
-
void ASurface::plotImage(SpriteResource *sprite, int frameNum, const Common::Point &pt) {
SpriteFrame *frame = sprite->getFrame(frameNum);
Common::Rect r(pt.x, pt.y, pt.x + frame->w, pt.y + frame->h);
@@ -195,81 +144,7 @@ void ASurface::plotImage(SpriteResource *sprite, int frameNum, const Common::Poi
}
}
-void ASurface::transBlitFrom(ASurface *src, const Common::Point &destPos) {
- if (getPixels() == nullptr)
- create(w, h);
-
- for (int yp = 0; yp < src->h; ++yp) {
- const byte *srcP = (const byte *)src->getBasePtr(0, yp);
- byte *destP = (byte *)getBasePtr(destPos.x, destPos.y + yp);
-
- for (int xp = 0; xp < this->w; ++xp, ++srcP, ++destP) {
- if (*srcP != TRANSPARENCY)
- *destP = *srcP;
- }
- }
-}
-
-void ASurface::transBlitFrom(ASurface *src, const Common::Rect &bounds) {
- const int SCALE_LIMIT = 0x100;
- int scaleX = SCALE_LIMIT * bounds.width() / src->w;
- int scaleY = SCALE_LIMIT * bounds.height() / src->h;
- int scaleXCtr = 0, scaleYCtr = 0;
-
- for (int yCtr = 0, destY = bounds.top; yCtr < src->h; ++yCtr) {
- // Handle skipping lines if Y scaling
- scaleYCtr += scaleY;
- if (scaleYCtr < SCALE_LIMIT)
- continue;
- scaleYCtr -= SCALE_LIMIT;
-
- // Handle off-screen lines
- if (destY >= this->h)
- break;
-
- if (destY >= 0) {
- // Handle drawing the line
- const byte *pSrc = (const byte *)src->getBasePtr(0, yCtr);
- byte *pDest = (byte *)getBasePtr(bounds.left, destY);
- scaleXCtr = 0;
- int x = bounds.left;
-
- for (int xCtr = 0; xCtr < src->w; ++xCtr, ++pSrc) {
- // Handle horizontal scaling
- scaleXCtr += scaleX;
- if (scaleXCtr < SCALE_LIMIT)
- continue;
- scaleXCtr -= SCALE_LIMIT;
-
- // Only handle on-screen pixels
- if (x >= this->w)
- break;
- if (x >= 0 && *pSrc != 0)
- *pDest = *pSrc;
-
- ++pDest;
- ++x;
- }
- }
-
- ++destY;
- }
-}
-
-void ASurface::transBlitFrom(ASurface &src) {
- blitFrom(src);
-}
-
-void ASurface::blitFrom(const Graphics::Surface &src) {
- assert(w >= src.w && h >= src.h);
- for (int y = 0; y < src.h; ++y) {
- const byte *srcP = (const byte *)src.getBasePtr(0, y);
- byte *destP = (byte *)getBasePtr(0, y);
- Common::copy(srcP, srcP + src.w, destP);
- }
-}
-
-void ASurface::copyBuffer(Graphics::Surface *src) {
+void ASurface::copyBuffer(Graphics::ManagedSurface *src) {
blitFrom(*src);
}
@@ -282,14 +157,11 @@ void ASurface::plotB(SpriteFrame *frame, const Common::Point &pt) {
}
void ASurface::sPlotF(SpriteFrame *frame, const Common::Rect &bounds) {
- transBlitFrom(frame, bounds);
+ transBlitFrom(*frame, Common::Rect(0, 0, frame->w, frame->h), bounds, TRANSPARENCY, false);
}
void ASurface::sPlotB(SpriteFrame *frame, const Common::Rect &bounds) {
- ASurface flippedFrame;
- frame->flipHorizontal(flippedFrame);
-
- transBlitFrom(&flippedFrame, bounds);
+ transBlitFrom(*frame, Common::Rect(0, 0, frame->w, frame->h), bounds, TRANSPARENCY, true);
}
void ASurface::copyBlock(ASurface *src, const Common::Rect &bounds) {
@@ -324,22 +196,22 @@ void ASurface::restoreBlock() {
}
void ASurface::drawRect() {
- Graphics::Surface::fillRect(Common::Rect(_orgX1, _orgY1, _orgX2, _orgY2), _lColor);
+ Graphics::ManagedSurface::fillRect(Common::Rect(_orgX1, _orgY1, _orgX2, _orgY2), _lColor);
}
void ASurface::drawLine(int x1, int y1, int x2, int y2, int col) {
- Graphics::Surface::drawLine(x1, y1, x2, y2, col);
+ Graphics::ManagedSurface::drawLine(x1, y1, x2, y2, col);
}
void ASurface::drawLine() {
- Graphics::Surface::drawLine(_orgX1, _orgY1, _orgX2, _orgY1, _lColor);
+ Graphics::ManagedSurface::drawLine(_orgX1, _orgY1, _orgX2, _orgY1, _lColor);
}
void ASurface::drawBox() {
- Graphics::Surface::drawLine(_orgX1, _orgY1, _orgX2, _orgY1, _lColor);
- Graphics::Surface::drawLine(_orgX1, _orgY2, _orgX2, _orgY2, _lColor);
- Graphics::Surface::drawLine(_orgX2, _orgY1, _orgX2, _orgY1, _lColor);
- Graphics::Surface::drawLine(_orgX2, _orgY2, _orgX2, _orgY2, _lColor);
+ Graphics::ManagedSurface::drawLine(_orgX1, _orgY1, _orgX2, _orgY1, _lColor);
+ Graphics::ManagedSurface::drawLine(_orgX1, _orgY2, _orgX2, _orgY2, _lColor);
+ Graphics::ManagedSurface::drawLine(_orgX2, _orgY1, _orgX2, _orgY1, _lColor);
+ Graphics::ManagedSurface::drawLine(_orgX2, _orgY2, _orgX2, _orgY2, _lColor);
}
void ASurface::flipHorizontal(ASurface &dest) {
@@ -373,4 +245,50 @@ void ASurface::moveBufferDown() {
Common::copy_backward(p, p + (pitch * (h - TILE_HEIGHT)), p + (pitch * h));
}
+bool ASurface::clip(Common::Rect &r) {
+ int skip;
+ _leftSkip = _rightSkip = 0;
+ _topSkip = _bottomSkip = 0;
+
+ if (r.left > _clipWidth || r.left < 0) {
+ if (r.left >= 0)
+ return true;
+
+ skip = -r.left;
+ r.setWidth(r.width() - skip);
+ _leftSkip = skip;
+ r.moveTo(0, r.top);
+ }
+
+ int right = r.right - 1;
+ if (right < 0)
+ return true;
+ else if (right > _clipWidth) {
+ skip = right - _clipWidth;
+ r.setWidth(r.width() - skip);
+ _rightSkip = skip;
+ }
+
+ if (r.top > _clipHeight || r.top < 0) {
+ if (r.top >= 0)
+ return true;
+
+ skip = -r.top;
+ r.setHeight(r.height() - skip);
+ _topSkip = skip;
+ r.moveTo(r.left, 0);
+ }
+
+ int bottom = r.bottom - 1;
+ if (bottom < 0)
+ return true;
+ else if (bottom > _clipHeight) {
+ skip = bottom - _clipHeight;
+ _bottomSkip = skip;
+ r.setHeight(r.height() - skip);
+ }
+
+ return false;
+}
+
} // End of namespace Access
diff --git a/engines/access/asurface.h b/engines/access/asurface.h
index dd05c8067b..ec18ec09c3 100644
--- a/engines/access/asurface.h
+++ b/engines/access/asurface.h
@@ -27,7 +27,7 @@
#include "common/array.h"
#include "common/memstream.h"
#include "common/rect.h"
-#include "graphics/surface.h"
+#include "graphics/managed_surface.h"
#include "access/data.h"
namespace Access {
@@ -35,7 +35,7 @@ namespace Access {
class SpriteResource;
class SpriteFrame;
-class ASurface : public Graphics::Surface {
+class ASurface : virtual public Graphics::ManagedSurface {
private:
Graphics::Surface _savedBlock;
@@ -61,14 +61,8 @@ public:
virtual ~ASurface();
- void create(uint16 width, uint16 height);
-
- bool empty() const { return w == 0 || h == 0 || pixels == nullptr; }
-
void clearBuffer();
- bool clip(Common::Rect &r);
-
void plotImage(SpriteResource *sprite, int frameNum, const Common::Point &pt);
/**
@@ -102,18 +96,8 @@ public:
virtual void drawLine();
virtual void drawBox();
-
- virtual void transBlitFrom(ASurface *src, const Common::Point &destPos);
-
- virtual void transBlitFrom(ASurface *src, const Common::Rect &bounds);
- virtual void transBlitFrom(ASurface &src);
-
- virtual void blitFrom(const Graphics::Surface &src);
-
- virtual void copyBuffer(Graphics::Surface *src);
-
- virtual void addDirtyRect(const Common::Rect &r) {}
+ virtual void copyBuffer(Graphics::ManagedSurface *src);
void copyTo(ASurface *dest);
@@ -126,6 +110,8 @@ public:
void moveBufferUp();
void moveBufferDown();
+
+ bool clip(Common::Rect &r);
};
class SpriteFrame : public ASurface {
diff --git a/engines/access/detection.cpp b/engines/access/detection.cpp
index 7494c9c047..2cd7e50f0f 100644
--- a/engines/access/detection.cpp
+++ b/engines/access/detection.cpp
@@ -146,7 +146,6 @@ SaveStateList AccessMetaEngine::listSaves(const char *target) const {
Access::AccessSavegameHeader header;
filenames = saveFileMan->listSavefiles(pattern);
- sort(filenames.begin(), filenames.end()); // Sort to get the files in numerical order
SaveStateList saveList;
for (Common::StringArray::const_iterator file = filenames.begin(); file != filenames.end(); ++file) {
@@ -167,6 +166,8 @@ SaveStateList AccessMetaEngine::listSaves(const char *target) const {
}
}
+ // Sort saves based on slot number.
+ Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator());
return saveList;
}
diff --git a/engines/access/detection_tables.h b/engines/access/detection_tables.h
index 9556cd9f67..7d9509ca43 100644
--- a/engines/access/detection_tables.h
+++ b/engines/access/detection_tables.h
@@ -49,7 +49,7 @@ static const AccessGameDescription gameDescriptions[] = {
AD_ENTRY1s("c00.ap", "aeb429ff015596144c0df06886c84825", 303753),
Common::ES_ESP,
Common::kPlatformDOS,
- ADGF_NO_FLAGS,
+ ADGF_UNSTABLE,
GUIO1(GUIO_NONE)
},
GType_Amazon,
diff --git a/engines/access/events.cpp b/engines/access/events.cpp
index d62b05c33f..21ff0d0928 100644
--- a/engines/access/events.cpp
+++ b/engines/access/events.cpp
@@ -115,7 +115,7 @@ void EventsManager::setCursor(CursorType cursorId) {
}
}
-void EventsManager::setCursorData(Graphics::Surface *src, const Common::Rect &r) {
+void EventsManager::setCursorData(Graphics::ManagedSurface *src, const Common::Rect &r) {
_invCursor.create(r.width(), r.height(), Graphics::PixelFormat::createFormatCLUT8());
_invCursor.copyRectToSurface(*src, 0, 0, r);
}
@@ -281,8 +281,7 @@ void EventsManager::nextFrame() {
// Give time to the debugger
_vm->_debugger->onFrame();
- // TODO: Refactor for dirty rects
- _vm->_screen->updateScreen();
+ _vm->_screen->update();
}
void EventsManager::nextTimer() {
diff --git a/engines/access/events.h b/engines/access/events.h
index b8c5f0ee5e..5acbb71c9d 100644
--- a/engines/access/events.h
+++ b/engines/access/events.h
@@ -100,7 +100,7 @@ public:
/**
* Set the image for the inventory cursor
*/
- void setCursorData(Graphics::Surface *src, const Common::Rect &r);
+ void setCursorData(Graphics::ManagedSurface *src, const Common::Rect &r);
/**
* Return the current cursor Id
diff --git a/engines/access/files.cpp b/engines/access/files.cpp
index b9c0f7080d..48276ee477 100644
--- a/engines/access/files.cpp
+++ b/engines/access/files.cpp
@@ -130,13 +130,13 @@ void FileManager::openFile(Resource *res, const Common::String &filename) {
error("Could not open file - %s", filename.c_str());
}
-void FileManager::loadScreen(Graphics::Surface *dest, int fileNum, int subfile) {
+void FileManager::loadScreen(Graphics::ManagedSurface *dest, int fileNum, int subfile) {
Resource *res = loadFile(fileNum, subfile);
handleScreen(dest, res);
delete res;
}
-void FileManager::handleScreen(Graphics::Surface *dest, Resource *res) {
+void FileManager::handleScreen(Graphics::ManagedSurface *dest, Resource *res) {
_vm->_screen->loadRawPalette(res->_stream);
if (_setPaletteFlag)
_vm->_screen->setPalette();
@@ -147,20 +147,17 @@ void FileManager::handleScreen(Graphics::Surface *dest, Resource *res) {
res->_size -= res->_stream->pos();
handleFile(res);
- if (dest != _vm->_screen)
- dest->w = _vm->_screen->w;
+ Graphics::Surface destSurface = dest->getSubArea(Common::Rect(0, 0,
+ _vm->_screen->w, _vm->_screen->h));
- if (dest->w == dest->pitch) {
- res->_stream->read((byte *)dest->getPixels(), dest->w * dest->h);
+ if (destSurface.w == destSurface.pitch) {
+ res->_stream->read((byte *)destSurface.getPixels(), destSurface.w * destSurface.h);
} else {
- for (int y = 0; y < dest->h; ++y) {
- byte *pDest = (byte *)dest->getBasePtr(0, y);
- res->_stream->read(pDest, dest->w);
+ for (int y = 0; y < destSurface.h; ++y) {
+ byte *pDest = (byte *)destSurface.getBasePtr(0, y);
+ res->_stream->read(pDest, destSurface.w);
}
}
-
- if (dest == _vm->_screen)
- _vm->_screen->addDirtyRect(Common::Rect(0, 0, dest->w, dest->h));
}
void FileManager::loadScreen(int fileNum, int subfile) {
diff --git a/engines/access/files.h b/engines/access/files.h
index d081934e91..61fccc2431 100644
--- a/engines/access/files.h
+++ b/engines/access/files.h
@@ -26,7 +26,7 @@
#include "common/scummsys.h"
#include "common/array.h"
#include "common/file.h"
-#include "graphics/surface.h"
+#include "graphics/managed_surface.h"
#include "access/decompress.h"
namespace Access {
@@ -81,7 +81,7 @@ private:
/**
* Handles loading a screen surface and palette with decoded resource
*/
- void handleScreen(Graphics::Surface *dest, Resource *res);
+ void handleScreen(Graphics::ManagedSurface *dest, Resource *res);
/**
* Open up a sub-file container file
@@ -133,7 +133,7 @@ public:
/**
* Load a screen resource onto a designated surface
*/
- void loadScreen(Graphics::Surface *dest, int fileNum, int subfile);
+ void loadScreen(Graphics::ManagedSurface *dest, int fileNum, int subfile);
};
} // End of namespace Access
diff --git a/engines/access/font.cpp b/engines/access/font.cpp
index 8af183f193..6ae65e43f0 100644
--- a/engines/access/font.cpp
+++ b/engines/access/font.cpp
@@ -151,13 +151,12 @@ void Font::drawString(ASurface *s, const Common::String &msg, const Common::Poin
int Font::drawChar(ASurface *s, char c, Common::Point &pt) {
Graphics::Surface &ch = _chars[c - ' '];
-
- s->addDirtyRect(Common::Rect(pt.x, pt.y, pt.x + ch.w, pt.y + ch.h));
+ Graphics::Surface dest = s->getSubArea(Common::Rect(pt.x, pt.y, pt.x + ch.w, pt.y + ch.h));
// Loop through the lines of the character
for (int y = 0; y < ch.h; ++y) {
byte *pSrc = (byte *)ch.getBasePtr(0, y);
- byte *pDest = (byte *)s->getBasePtr(pt.x, pt.y + y);
+ byte *pDest = (byte *)dest.getBasePtr(0, y);
// Loop through the horizontal pixels of the line
for (int x = 0; x < ch.w; ++x, ++pSrc, ++pDest) {
diff --git a/engines/access/screen.cpp b/engines/access/screen.cpp
index aa15abd59a..9700640b71 100644
--- a/engines/access/screen.cpp
+++ b/engines/access/screen.cpp
@@ -69,8 +69,6 @@ void Screen::clearScreen() {
clearBuffer();
if (_vesaMode)
_vm->_clearSummaryFlag = true;
-
- addDirtyRect(Common::Rect(0, 0, this->w, this->h));
}
void Screen::setDisplayScan() {
@@ -89,28 +87,14 @@ void Screen::setPanel(int num) {
_msVirtualOffset = _virtualOffsetsTable[num];
}
-void Screen::updateScreen() {
+void Screen::update() {
if (_vm->_startup >= 0) {
if (--_vm->_startup == -1)
_fadeIn = true;
return;
}
-
- // Merge the dirty rects
- mergeDirtyRects();
-
- // Loop through copying dirty areas to the physical screen
- Common::List<Common::Rect>::iterator i;
- for (i = _dirtyRects.begin(); i != _dirtyRects.end(); ++i) {
- const Common::Rect &r = *i;
- const byte *srcP = (const byte *)getBasePtr(r.left, r.top);
- g_system->copyRectToScreen(srcP, this->pitch, r.left, r.top,
- r.width(), r.height());
- }
-
- // Signal the physical screen to update
- g_system->updateScreen();
- _dirtyRects.clear();
+ markAllDirty();//****DEBUG****
+ Graphics::Screen::update();
}
void Screen::setInitialPalettte() {
@@ -153,7 +137,7 @@ void Screen::loadRawPalette(Common::SeekableReadStream *stream) {
void Screen::updatePalette() {
g_system->getPaletteManager()->setPalette(&_tempPalette[0], 0, PALETTE_COUNT);
- updateScreen();
+ update();
}
void Screen::savePalette() {
@@ -293,22 +277,7 @@ void Screen::drawBox() {
ASurface::drawBox();
}
-void Screen::transBlitFrom(ASurface *src, const Common::Point &destPos) {
- addDirtyRect(Common::Rect(destPos.x, destPos.y, destPos.x + src->w, destPos.y + src->h));
- ASurface::transBlitFrom(src, destPos);
-}
-
-void Screen::transBlitFrom(ASurface *src, const Common::Rect &bounds) {
- addDirtyRect(bounds);
- ASurface::transBlitFrom(src, bounds);
-}
-
-void Screen::blitFrom(const Graphics::Surface &src) {
- addDirtyRect(Common::Rect(0, 0, src.w, src.h));
- ASurface::blitFrom(src);
-}
-
-void Screen::copyBuffer(Graphics::Surface *src) {
+void Screen::copyBuffer(Graphics::ManagedSurface *src) {
addDirtyRect(Common::Rect(0, 0, src->w, src->h));
ASurface::copyBuffer(src);
}
@@ -349,51 +318,7 @@ void Screen::cyclePaletteBackwards() {
}
void Screen::flashPalette(int count) {
- warning("TODO: Implement flashPalette");
-}
-
-void Screen::addDirtyRect(const Common::Rect &r) {
- _dirtyRects.push_back(r);
- assert(r.isValidRect() && r.width() > 0 && r.height() > 0);
-}
-
-void Screen::mergeDirtyRects() {
- Common::List<Common::Rect>::iterator rOuter, rInner;
-
- // Ensure dirty rect list has at least two entries
- rOuter = _dirtyRects.begin();
- for (int i = 0; i < 2; ++i, ++rOuter) {
- if (rOuter == _dirtyRects.end())
- return;
- }
-
- // Process the dirty rect list to find any rects to merge
- 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
-
- unionRectangle(*rOuter, *rOuter, *rInner);
-
- // remove the inner rect from the list
- _dirtyRects.erase(rInner);
-
- // move back to beginning of list
- rInner = rOuter;
- }
- }
- }
+ // No implementation needed in ScummVM
}
-bool Screen::unionRectangle(Common::Rect &destRect, const Common::Rect &src1, const Common::Rect &src2) {
- destRect = src1;
- destRect.extend(src2);
-
- return !destRect.isEmpty();
-}
-
-
} // End of namespace Access
diff --git a/engines/access/screen.h b/engines/access/screen.h
index 6fa0fe3812..a022741f91 100644
--- a/engines/access/screen.h
+++ b/engines/access/screen.h
@@ -26,15 +26,13 @@
#include "common/scummsys.h"
#include "common/rect.h"
#include "common/stream.h"
+#include "graphics/screen.h"
#include "access/asurface.h"
namespace Access {
class AccessEngine;
-#define PALETTE_COUNT 256
-#define PALETTE_SIZE (256 * 3)
-
struct ScreenSave {
int _clipWidth;
int _clipHeight;
@@ -47,7 +45,7 @@ struct ScreenSave {
int _screenYOff;
};
-class Screen : public ASurface {
+class Screen : public virtual ASurface, public virtual Graphics::Screen {
private:
AccessEngine *_vm;
byte _tempPalette[PALETTE_SIZE];
@@ -66,10 +64,6 @@ private:
Common::List<Common::Rect> _dirtyRects;
void updatePalette();
-
- void mergeDirtyRects();
-
- bool unionRectangle(Common::Rect &destRect, const Common::Rect &src1, const Common::Rect &src2);
public:
int _vesaMode;
int _startColor, _numColors;
@@ -87,6 +81,11 @@ public:
bool _screenChangeFlag;
bool _fadeIn;
public:
+ /**
+ * Updates the screen
+ */
+ virtual void update();
+
virtual void copyBlock(ASurface *src, const Common::Rect &bounds);
virtual void restoreBlock();
@@ -95,15 +94,7 @@ public:
virtual void drawBox();
- virtual void transBlitFrom(ASurface *src, const Common::Point &destPos);
-
- virtual void transBlitFrom(ASurface *src, const Common::Rect &bounds);
-
- virtual void blitFrom(const Graphics::Surface &src);
-
- virtual void copyBuffer(Graphics::Surface *src);
-
- virtual void addDirtyRect(const Common::Rect &r);
+ virtual void copyBuffer(Graphics::ManagedSurface *src);
public:
Screen(AccessEngine *vm);
@@ -114,11 +105,6 @@ public:
void setPanel(int num);
/**
- * Update the underlying screen
- */
- void updateScreen();
-
- /**
* Fade out screen
*/
void forceFadeOut();
diff --git a/engines/access/scripts.cpp b/engines/access/scripts.cpp
index 7c354e78e5..38313640f1 100644
--- a/engines/access/scripts.cpp
+++ b/engines/access/scripts.cpp
@@ -1005,10 +1005,7 @@ void Scripts::cmdFreeSound() {
} while (!_vm->shouldQuit() && sound.isSFXPlaying());
// Free the sounds
- while (sound._soundTable.size() > 0) {
- delete sound._soundTable[0]._res;
- sound._soundTable.remove_at(0);
- }
+ sound.freeSounds();
}
}
diff --git a/engines/access/video.cpp b/engines/access/video.cpp
index 5fc5f6762c..e3ff457c3b 100644
--- a/engines/access/video.cpp
+++ b/engines/access/video.cpp
@@ -157,7 +157,7 @@ void VideoPlayer::playVideo() {
// If the video is playing on the screen surface, add a dirty rect
if (_vidSurface == _vm->_screen)
- _vm->_screen->addDirtyRect(_videoBounds);
+ _vm->_screen->markAllDirty();
getFrame();
if (++_videoFrame == _frameCount) {
diff --git a/engines/access/video/movie_decoder.cpp b/engines/access/video/movie_decoder.cpp
index 05ec25d54c..1406e549ad 100644
--- a/engines/access/video/movie_decoder.cpp
+++ b/engines/access/video/movie_decoder.cpp
@@ -719,7 +719,7 @@ bool AccessEngine::playMovie(const Common::String &filename, const Common::Point
g_system->getPaletteManager()->setPalette(palette, 0, 256);
}
- _screen->updateScreen();
+ _screen->update();
}
}
diff --git a/engines/advancedDetector.cpp b/engines/advancedDetector.cpp
index 72629a833e..7a09f662d1 100644
--- a/engines/advancedDetector.cpp
+++ b/engines/advancedDetector.cpp
@@ -41,8 +41,8 @@ static GameDescriptor toGameDescriptor(const ADGameDescription &g, const PlainGa
title = g.extra;
extra = "";
} else {
- while (sg->gameid) {
- if (!scumm_stricmp(g.gameid, sg->gameid))
+ while (sg->gameId) {
+ if (!scumm_stricmp(g.gameId, sg->gameId))
title = sg->description;
sg++;
}
@@ -56,7 +56,7 @@ static GameDescriptor toGameDescriptor(const ADGameDescription &g, const PlainGa
else if (g.flags & ADGF_TESTING)
gsl = kTestingGame;
- GameDescriptor gd(g.gameid, title, g.language, g.platform, 0, gsl);
+ GameDescriptor gd(g.gameId, title, g.language, g.platform, 0, gsl);
gd.updateDesc(extra);
return gd;
}
@@ -89,21 +89,38 @@ static Common::String generatePreferredTarget(const Common::String &id, const AD
return res;
}
+static Common::String sanitizeName(const char *name) {
+ Common::String res;
+
+ while (*name) {
+ if (Common::isAlnum(*name))
+ res += tolower(*name);
+ name++;
+ }
+
+ return res;
+}
+
void AdvancedMetaEngine::updateGameDescriptor(GameDescriptor &desc, const ADGameDescription *realDesc) const {
- if (_singleid != NULL) {
+ if (_singleId != NULL) {
desc["preferredtarget"] = desc["gameid"];
- desc["gameid"] = _singleid;
+ desc["gameid"] = _singleId;
}
if (!desc.contains("preferredtarget"))
desc["preferredtarget"] = desc["gameid"];
+ if (realDesc->flags & ADGF_AUTOGENTARGET) {
+ if (*realDesc->extra)
+ desc["preferredtarget"] = sanitizeName(realDesc->extra);
+ }
+
desc["preferredtarget"] = generatePreferredTarget(desc["preferredtarget"], realDesc);
if (_flags & kADFlagUseExtraAsHint)
desc["extra"] = realDesc->extra;
- desc.setGUIOptions(realDesc->guioptions + _guioptions);
+ desc.setGUIOptions(realDesc->guiOptions + _guiOptions);
desc.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(realDesc->language));
if (realDesc->flags & ADGF_ADDENGLISH)
@@ -149,7 +166,7 @@ GameList AdvancedMetaEngine::detectGames(const Common::FSList &fslist) const {
// Use fallback detector if there were no matches by other means
const ADGameDescription *fallbackDesc = fallbackDetect(allFiles, fslist);
if (fallbackDesc != 0) {
- GameDescriptor desc(toGameDescriptor(*fallbackDesc, _gameids));
+ GameDescriptor desc(toGameDescriptor(*fallbackDesc, _gameIds));
updateGameDescriptor(desc, fallbackDesc);
detectedGames.push_back(desc);
}
@@ -157,7 +174,7 @@ GameList AdvancedMetaEngine::detectGames(const Common::FSList &fslist) const {
// Otherwise use the found matches
cleanupPirated(matches);
for (uint i = 0; i < matches.size(); i++) {
- GameDescriptor desc(toGameDescriptor(*matches[i], _gameids));
+ GameDescriptor desc(toGameDescriptor(*matches[i], _gameIds));
updateGameDescriptor(desc, matches[i]);
detectedGames.push_back(desc);
}
@@ -252,10 +269,10 @@ Common::Error AdvancedMetaEngine::createInstance(OSystem *syst, Engine **engine)
if (cleanupPirated(matches))
return Common::kNoGameDataFoundError;
- if (_singleid == NULL) {
+ if (_singleId == NULL) {
// Find the first match with correct gameid.
for (uint i = 0; i < matches.size(); i++) {
- if (matches[i]->gameid == gameid) {
+ if (matches[i]->gameId == gameid) {
agdDesc = matches[i];
break;
}
@@ -270,7 +287,7 @@ Common::Error AdvancedMetaEngine::createInstance(OSystem *syst, Engine **engine)
if (agdDesc != 0) {
// Seems we found a fallback match. But first perform a basic
// sanity check: the gameid must match.
- if (_singleid == NULL && agdDesc->gameid != gameid)
+ if (_singleId == NULL && agdDesc->gameId != gameid)
agdDesc = 0;
}
}
@@ -284,9 +301,9 @@ Common::Error AdvancedMetaEngine::createInstance(OSystem *syst, Engine **engine)
if (agdDesc->flags & ADGF_ADDENGLISH)
lang += " " + getGameGUIOptionsDescriptionLanguage(Common::EN_ANY);
- Common::updateGameGUIOptions(agdDesc->guioptions + _guioptions, lang);
+ Common::updateGameGUIOptions(agdDesc->guiOptions + _guiOptions, lang);
- GameDescriptor gameDescriptor = toGameDescriptor(*agdDesc, _gameids);
+ GameDescriptor gameDescriptor = toGameDescriptor(*agdDesc, _gameIds);
bool showTestingWarning = false;
@@ -407,7 +424,7 @@ ADGameDescList AdvancedMetaEngine::detectGame(const Common::FSNode &parent, cons
// Check which files are included in some ADGameDescription *and* are present.
// Compute MD5s and file sizes for these files.
- for (descPtr = _gameDescriptors; ((const ADGameDescription *)descPtr)->gameid != 0; descPtr += _descItemSize) {
+ for (descPtr = _gameDescriptors; ((const ADGameDescription *)descPtr)->gameId != 0; descPtr += _descItemSize) {
g = (const ADGameDescription *)descPtr;
for (fileDesc = g->filesDescriptions; fileDesc->fileName; fileDesc++) {
@@ -430,7 +447,7 @@ ADGameDescList AdvancedMetaEngine::detectGame(const Common::FSNode &parent, cons
// MD5 based matching
uint i;
- for (i = 0, descPtr = _gameDescriptors; ((const ADGameDescription *)descPtr)->gameid != 0; descPtr += _descItemSize, ++i) {
+ for (i = 0, descPtr = _gameDescriptors; ((const ADGameDescription *)descPtr)->gameId != 0; descPtr += _descItemSize, ++i) {
g = (const ADGameDescription *)descPtr;
bool fileMissing = false;
@@ -487,7 +504,7 @@ ADGameDescList AdvancedMetaEngine::detectGame(const Common::FSNode &parent, cons
gotAnyMatchesWithAllFiles = true;
if (!fileMissing) {
- debug(2, "Found game: %s (%s %s/%s) (%d)", g->gameid, g->extra,
+ debug(2, "Found game: %s (%s %s/%s) (%d)", g->gameId, g->extra,
getPlatformDescription(g->platform), getLanguageDescription(g->language), i);
if (curFilesMatched > maxFilesMatched) {
@@ -503,7 +520,7 @@ ADGameDescList AdvancedMetaEngine::detectGame(const Common::FSNode &parent, cons
}
} else {
- debug(5, "Skipping game: %s (%s %s/%s) (%d)", g->gameid, g->extra,
+ debug(5, "Skipping game: %s (%s %s/%s) (%d)", g->gameId, g->extra,
getPlatformDescription(g->platform), getLanguageDescription(g->language), i);
}
}
@@ -543,7 +560,7 @@ const ADGameDescription *AdvancedMetaEngine::detectGameFilebased(const FileMap &
}
if (!fileMissing) {
- debug(4, "Matched: %s", agdesc->gameid);
+ debug(4, "Matched: %s", agdesc->gameId);
if (numMatchedFiles > maxNumMatchedFiles) {
matchedDesc = agdesc;
@@ -568,27 +585,27 @@ const ADGameDescription *AdvancedMetaEngine::detectGameFilebased(const FileMap &
}
GameList AdvancedMetaEngine::getSupportedGames() const {
- if (_singleid != NULL) {
+ if (_singleId != NULL) {
GameList gl;
- const PlainGameDescriptor *g = _gameids;
- while (g->gameid) {
- if (0 == scumm_stricmp(_singleid, g->gameid)) {
- gl.push_back(GameDescriptor(g->gameid, g->description));
+ const PlainGameDescriptor *g = _gameIds;
+ while (g->gameId) {
+ if (0 == scumm_stricmp(_singleId, g->gameId)) {
+ gl.push_back(GameDescriptor(g->gameId, g->description));
return gl;
}
g++;
}
- error("Engine %s doesn't have its singleid specified in ids list", _singleid);
+ error("Engine %s doesn't have its singleid specified in ids list", _singleId);
}
- return GameList(_gameids);
+ return GameList(_gameIds);
}
-GameDescriptor AdvancedMetaEngine::findGame(const char *gameid) const {
+GameDescriptor AdvancedMetaEngine::findGame(const char *gameId) const {
// First search the list of supported gameids for a match.
- const PlainGameDescriptor *g = findPlainGameDescriptor(gameid, _gameids);
+ const PlainGameDescriptor *g = findPlainGameDescriptor(gameId, _gameIds);
if (g)
return GameDescriptor(*g);
@@ -596,14 +613,14 @@ GameDescriptor AdvancedMetaEngine::findGame(const char *gameid) const {
return GameDescriptor();
}
-AdvancedMetaEngine::AdvancedMetaEngine(const void *descs, uint descItemSize, const PlainGameDescriptor *gameids, const ADExtraGuiOptionsMap *extraGuiOptions)
- : _gameDescriptors((const byte *)descs), _descItemSize(descItemSize), _gameids(gameids),
+AdvancedMetaEngine::AdvancedMetaEngine(const void *descs, uint descItemSize, const PlainGameDescriptor *gameIds, const ADExtraGuiOptionsMap *extraGuiOptions)
+ : _gameDescriptors((const byte *)descs), _descItemSize(descItemSize), _gameIds(gameIds),
_extraGuiOptions(extraGuiOptions) {
_md5Bytes = 5000;
- _singleid = NULL;
+ _singleId = NULL;
_flags = 0;
- _guioptions = GUIO_NONE;
+ _guiOptions = GUIO_NONE;
_maxScanDepth = 1;
_directoryGlobs = NULL;
}
diff --git a/engines/advancedDetector.h b/engines/advancedDetector.h
index ad551698f6..ab3ec22bdc 100644
--- a/engines/advancedDetector.h
+++ b/engines/advancedDetector.h
@@ -81,6 +81,7 @@ typedef Common::HashMap<Common::String, ADFileProperties, Common::IgnoreCase_Has
enum ADGameFlags {
ADGF_NO_FLAGS = 0,
+ ADGF_AUTOGENTARGET = (1 << 20), // automatically generate gameid from extra
ADGF_UNSTABLE = (1 << 21), // flag to designate not yet officially-supported games that are not fit for public testing
ADGF_TESTING = (1 << 22), // flag to designate not yet officially-supported games that are fit for public testing
ADGF_PIRATED = (1 << 23), ///< flag to designate well known pirated versions with cracks
@@ -94,7 +95,7 @@ enum ADGameFlags {
};
struct ADGameDescription {
- const char *gameid;
+ const char *gameId;
const char *extra;
ADGameFileDescription filesDescriptions[14];
Common::Language language;
@@ -107,7 +108,7 @@ struct ADGameDescription {
*/
uint32 flags;
- const char *guioptions;
+ const char *guiOptions;
};
/**
@@ -191,7 +192,7 @@ protected:
* A list of all gameids (and their corresponding descriptions) supported
* by this engine.
*/
- const PlainGameDescriptor *_gameids;
+ const PlainGameDescriptor *_gameIds;
/**
* A map containing all the extra game GUI options the engine supports.
@@ -219,7 +220,7 @@ protected:
* address a more generic problem. We should find a better way to
* disambiguate gameids.
*/
- const char *_singleid;
+ const char *_singleId;
/**
* A bitmask of flags which can be used to configure the behavior
@@ -233,7 +234,7 @@ protected:
* entry in addition to per-game options. Refer to GameGUIOption
* enum for the list.
*/
- Common::String _guioptions;
+ Common::String _guiOptions;
/**
* Maximum depth of directories to look up.
@@ -251,7 +252,7 @@ protected:
const char * const *_directoryGlobs;
public:
- AdvancedMetaEngine(const void *descs, uint descItemSize, const PlainGameDescriptor *gameids, const ADExtraGuiOptionsMap *extraGuiOptions = 0);
+ AdvancedMetaEngine(const void *descs, uint descItemSize, const PlainGameDescriptor *gameIds, const ADExtraGuiOptionsMap *extraGuiOptions = 0);
/**
* Returns list of targets supported by the engine.
@@ -259,7 +260,7 @@ public:
*/
virtual GameList getSupportedGames() const;
- virtual GameDescriptor findGame(const char *gameid) const;
+ virtual GameDescriptor findGame(const char *gameId) const;
virtual GameList detectGames(const Common::FSList &fslist) const;
diff --git a/engines/agi/agi.cpp b/engines/agi/agi.cpp
index bcf84840d2..6e63cd3e71 100644
--- a/engines/agi/agi.cpp
+++ b/engines/agi/agi.cpp
@@ -283,18 +283,7 @@ void AgiBase::initRenderMode() {
switch (platform) {
case Common::kPlatformDOS:
- switch (configRenderMode) {
- case Common::kRenderCGA:
- _renderMode = Common::kRenderCGA;
- break;
- // Hercules is not supported atm
- //case Common::kRenderHercA:
- //case Common::kRenderHercG:
- // _renderMode = Common::kRenderHercG;
- // break;
- default:
- break;
- }
+ // Keep EGA
break;
case Common::kPlatformAmiga:
_renderMode = Common::kRenderAmiga;
@@ -323,6 +312,12 @@ void AgiBase::initRenderMode() {
case Common::kRenderVGA:
_renderMode = Common::kRenderVGA;
break;
+ case Common::kRenderHercG:
+ _renderMode = Common::kRenderHercG;
+ break;
+ case Common::kRenderHercA:
+ _renderMode = Common::kRenderHercA;
+ break;
case Common::kRenderAmiga:
_renderMode = Common::kRenderAmiga;
break;
@@ -401,6 +396,11 @@ AgiEngine::AgiEngine(OSystem *syst, const AGIGameDescription *gameDesc) : AgiBas
setupOpcodes();
_game._curLogic = NULL;
+ _veryFirstInitialCycle = true;
+ _instructionCounter = 0;
+ resetGetVarSecondsHeuristic();
+
+ _setVolumeBrokenFangame = false; // for further study see AgiEngine::setVolumeViaScripts()
_lastSaveTime = 0;
@@ -461,7 +461,7 @@ void AgiEngine::initialize() {
_console = new Console(this);
_words = new Words(this);
_font = new GfxFont(this);
- _gfx = new GfxMgr(this);
+ _gfx = new GfxMgr(this, _font);
_sound = new SoundMgr(this, _mixer);
_picture = new PictureMgr(this, _gfx);
_sprites = new SpritesMgr(this, _gfx);
@@ -470,6 +470,8 @@ void AgiEngine::initialize() {
_inventory = new InventoryMgr(this, _gfx, _text, _systemUI);
_font->init();
+ _gfx->initVideo();
+
_text->init(_systemUI);
_game.gameFlags = 0;
@@ -478,8 +480,6 @@ void AgiEngine::initialize() {
_game.name[0] = '\0';
- _gfx->initVideo();
-
_lastSaveTime = 0;
debugC(2, kDebugLevelMain, "Detect game");
@@ -507,19 +507,6 @@ void AgiEngine::redrawScreen() {
_text->promptRedraw();
}
-// Adjust a given coordinate to the local game screen
-// Used on mouse cursor coordinates before passing them to scripts
-void AgiEngine::adjustPosToGameScreen(int16 &x, int16 &y) {
- x = x / 2; // 320 -> 160
- y = y - _gfx->getRenderStartOffsetY(); // remove status bar line
- if (y < 0) {
- y = 0;
- }
- if (y >= SCRIPT_HEIGHT) {
- y = SCRIPT_HEIGHT + 1; // 1 beyond
- }
-}
-
AgiEngine::~AgiEngine() {
agiDeinit();
delete _loader;
@@ -563,17 +550,28 @@ void AgiEngine::syncSoundSettings() {
setVolumeViaSystemSetting();
}
+// WORKAROUND:
+// Sometimes Sierra printed some text on the screen and did a room change immediately afterwards expecting the
+// interpreter to load the data for a bit of time. This of course doesn't happen in our AGI, so we try to
+// detect such situations via heuristic and then delay the game for a bit.
+// In those cases a wait mouse cursor will be shown.
+//
// Scenes that need this:
//
+// Gold Rush:
+// - During Stagecoach path, after getting solving the steep hill "Congratulations!!!" (NewRoom)
+// - when following your mule "Yet right on his tail!!!" (NewRoom/NewPicture - but room 123 stays the same)
// Manhunter 1:
// - intro text screen (DrawPic)
// - MAD "zooming in..." during intro and other scenes, for example room 124 (NewRoom)
// The NewRoom call is not done during the same cycle as the "zooming in..." print call.
// Space Quest 1:
// - right at the start of the game (NewRoom)
+// - right at the end of the asteroids "That was mighty close!" (NewRoom)
// Space Quest 2
// - right at the start of the game (NewRoom)
// - after exiting the very first room, a message pops up, that isn't readable without it (NewRoom)
+// - Climbing into shuttle on planet Labion. "You open the hatch and head on in." (NewRoom)
// Games, that must not be triggered:
@@ -590,7 +588,14 @@ void AgiEngine::nonBlockingText_Forget() {
_game.nonBlockingTextShown = false;
_game.nonBlockingTextCyclesLeft = 0;
}
-void AgiEngine::nonBlockingText_CycleDone() {
+
+void AgiEngine::artificialDelay_Reset() {
+ nonBlockingText_Forget();
+ _artificialDelayCurrentRoom = -1;
+ _artificialDelayCurrentPicture = -1;
+}
+
+void AgiEngine::artificialDelay_CycleDone() {
if (_game.nonBlockingTextCyclesLeft) {
_game.nonBlockingTextCyclesLeft--;
@@ -601,30 +606,97 @@ void AgiEngine::nonBlockingText_CycleDone() {
}
}
-void AgiEngine::loadingTrigger_NewRoom(int16 newRoomNr) {
- if (_game.nonBlockingTextShown) {
- _game.nonBlockingTextShown = false;
+// WORKAROUND:
+// On Apple IIgs, there are situations like for example the Police Quest 1 intro, where music is playing
+// and then the scripts switch to a new room, expecting it to load for a bit of time. In ScummVM this results
+// in music getting cut off, because our loading is basically done in an instant. This also happens in the
+// original interpreter, when you use a faster CPU in emulation.
+//
+// That's why there is an additional table, where one can add such situations to it.
+// These issues are basically impossible to detect, because sometimes music is also supposed to play throughout
+// multiple rooms.
+//
+// Normally all text-based issues should get detected by the current heuristic. Do not add those in here.
+
+// script, description, signature patch
+static const AgiArtificialDelayEntry artificialDelayTable[] = {
+ { GID_GOLDRUSH, Common::kPlatformApple2GS, ARTIFICIALDELAYTYPE_NEWROOM, 14, 21, 2200 }, // Stagecoach path: right after getting on it in Brooklyn
+ { GID_PQ1, Common::kPlatformApple2GS, ARTIFICIALDELAYTYPE_NEWPICTURE, 1, 2, 2200 }, // Intro: music track is supposed to finish before credits screen. Developers must have assumed that room loading would take that long.
+ { GID_MH1, Common::kPlatformApple2GS, ARTIFICIALDELAYTYPE_NEWPICTURE, 155, 183, 2200 }, // Happens, when hitting fingers at bar
+ { GID_AGIDEMO, Common::kPlatformUnknown, ARTIFICIALDELAYTYPE_END, -1, -1, 0 }
+};
- int16 curRoomNr = getVar(VM_VAR_CURRENT_ROOM);
+uint16 AgiEngine::artificialDelay_SearchTable(AgiArtificialDelayTriggerType triggerType, int16 orgNr, int16 newNr) {
+ if (getPlatform() != Common::kPlatformApple2GS) {
+ return 0;
+ }
+
+ const AgiArtificialDelayEntry *delayEntry = artificialDelayTable;
- if (newRoomNr != curRoomNr) {
- if (!_game.automaticRestoreGame) {
- // wait a bit, we detected non-blocking text
- wait(2000, true); // 2 seconds, set busy
+ while (delayEntry->triggerType != ARTIFICIALDELAYTYPE_END) {
+ if (triggerType == delayEntry->triggerType) {
+ if ((orgNr == delayEntry->orgNr) && (newNr == delayEntry->newNr)) {
+ if ((getGameID() == delayEntry->gameId) && (getPlatform() == delayEntry->platform)) {
+ warning("artificial delay forced");
+ return delayEntry->millisecondsDelay;
+ }
}
}
+
+ delayEntry++;
}
+ return 0;
}
-void AgiEngine::loadingTrigger_DrawPicture() {
- if (_game.nonBlockingTextShown) {
- _game.nonBlockingTextShown = false;
+void AgiEngine::artificialDelayTrigger_NewRoom(int16 newRoomNr) {
+ uint16 millisecondsDelay = 0;
- if (!_game.automaticRestoreGame) {
- // wait a bit, we detected non-blocking text
- wait(2000, true); // 2 seconds, set busy
+ //warning("artificial delay trigger: room %d -> new room %d", _artificialDelayCurrentRoom, newRoomNr);
+
+ if (!_game.automaticRestoreGame) {
+ millisecondsDelay = artificialDelay_SearchTable(ARTIFICIALDELAYTYPE_NEWROOM, _artificialDelayCurrentRoom, newRoomNr);
+
+ if (_game.nonBlockingTextShown) {
+ if (newRoomNr != _artificialDelayCurrentRoom) {
+ if (millisecondsDelay < 2000) {
+ // wait a bit, we detected non-blocking text
+ millisecondsDelay = 2000; // 2 seconds
+ }
+ }
+ }
+
+ if (millisecondsDelay) {
+ wait(millisecondsDelay, true); // set busy mouse cursor
+ _game.nonBlockingTextShown = false;
+ }
+ }
+
+ _artificialDelayCurrentRoom = newRoomNr;
+}
+
+void AgiEngine::artificialDelayTrigger_DrawPicture(int16 newPictureNr) {
+ uint16 millisecondsDelay = 0;
+
+ //warning("artificial delay trigger: picture %d -> new picture %d", _artificialDelayCurrentPicture, newPictureNr);
+
+ if (!_game.automaticRestoreGame) {
+ millisecondsDelay = artificialDelay_SearchTable(ARTIFICIALDELAYTYPE_NEWPICTURE, _artificialDelayCurrentPicture, newPictureNr);
+
+ if (_game.nonBlockingTextShown) {
+ if (newPictureNr != _artificialDelayCurrentPicture) {
+ if (millisecondsDelay < 2000) {
+ // wait a bit, we detected non-blocking text
+ millisecondsDelay = 2000; // 2 seconds, set busy
+ }
+ }
+ }
+
+ if (millisecondsDelay) {
+ wait(millisecondsDelay, true); // set busy mouse cursor
+ _game.nonBlockingTextShown = false;
}
}
+ _artificialDelayCurrentPicture = newPictureNr;
}
} // End of namespace Agi
diff --git a/engines/agi/agi.h b/engines/agi/agi.h
index 93017af099..b288557f57 100644
--- a/engines/agi/agi.h
+++ b/engines/agi/agi.h
@@ -148,17 +148,17 @@ enum BooterDisks {
// position and position.v.
//
enum AgiGameFeatures {
- GF_AGIMOUSE = (1 << 0),
+ GF_AGIMOUSE = (1 << 0), // this disables "Click-to-walk mouse interface"
GF_AGDS = (1 << 1),
- GF_AGI256 = (1 << 2),
- GF_AGI256_2 = (1 << 3),
- GF_AGIPAL = (1 << 4),
- GF_MACGOLDRUSH = (1 << 5),
- GF_FANMADE = (1 << 6),
- GF_MENUS = (1 << 7),
- GF_ESCPAUSE = (1 << 8),
+ GF_AGI256 = (1 << 2), // marks fanmade AGI-256 games
+ GF_AGI256_2 = (1 << 3), // marks fanmade AGI-256-2 games
+ GF_AGIPAL = (1 << 4), // marks game using fanmade AGIPAL extension
+ GF_MACGOLDRUSH = (1 << 5), // use "grdir" instead of "dir" for volume loading
+ GF_FANMADE = (1 << 6), // marks fanmade games
+ GF_MENUS = (1 << 7), // not used anymore
+ GF_ESCPAUSE = (1 << 8), // not used anymore, we detect this internally
GF_OLDAMIGAV20 = (1 << 9),
- GF_CLIPCOORDS = (1 << 10),
+ GF_CLIPCOORDS = (1 << 10), // not used atm
GF_2GSOLDSOUND = (1 << 11)
};
@@ -704,6 +704,21 @@ public:
}
};
+enum AgiArtificialDelayTriggerType {
+ ARTIFICIALDELAYTYPE_NEWROOM = 0,
+ ARTIFICIALDELAYTYPE_NEWPICTURE = 1,
+ ARTIFICIALDELAYTYPE_END = -1
+};
+
+struct AgiArtificialDelayEntry {
+ uint32 gameId;
+ Common::Platform platform;
+ AgiArtificialDelayTriggerType triggerType;
+ int16 orgNr;
+ int16 newNr;
+ uint16 millisecondsDelay;
+};
+
typedef void (*AgiCommand)(AgiGame *state, AgiEngine *vm, uint8 *p);
class AgiEngine : public AgiBase {
@@ -724,8 +739,6 @@ public:
Common::Error loadGameState(int slot);
Common::Error saveGameState(int slot, const Common::String &description);
- void adjustPosToGameScreen(int16 &x, int16 &y);
-
private:
int _keyQueue[KEY_QUEUE_SIZE];
int _keyQueueStart;
@@ -746,7 +759,7 @@ public:
SavedGameSlotIdArray getSavegameSlotIds();
Common::String getSavegameFilename(int16 slotId) const;
- bool getSavegameInformation(int16 slotId, Common::String &saveDescription, uint32 &saveDate, uint16 &saveTime, bool &saveIsValid);
+ bool getSavegameInformation(int16 slotId, Common::String &saveDescription, uint32 &saveDate, uint32 &saveTime, bool &saveIsValid);
int saveGame(const Common::String &fileName, const Common::String &descriptionString);
int loadGame(const Common::String &fileName, bool checkId = true);
@@ -847,6 +860,21 @@ public:
int testIfCode(int);
void executeAgiCommand(uint8, uint8 *);
+private:
+ bool _veryFirstInitialCycle; /**< signals, that currently the very first cycle is executed (restarts, etc. do not count!) */
+ uint32 _instructionCounter; /**< counts every instruction, that got executed, can wrap around */
+
+ bool _setVolumeBrokenFangame;
+
+ void resetGetVarSecondsHeuristic();
+ void getVarSecondsHeuristicTrigger();
+ uint32 _getVarSecondsHeuristicLastInstructionCounter; /**< last time VM_VAR_SECONDS were read */
+ uint16 _getVarSecondsHeuristicCounter; /**< how many times heuristic was triggered */
+
+ uint32 _playTimeInSecondsAdjust; /**< milliseconds to adjust for calculating current play time in seconds, see setVarSecondsTrigger() */
+
+ void setVarSecondsTrigger(byte newSeconds);
+
public:
// Some submethods of testIfCode
void skipInstruction(byte op);
@@ -899,6 +927,8 @@ private:
void checkMotion(ScreenObjEntry *screenObj);
public:
+ void motionActivated(ScreenObjEntry *screenObj);
+ void cyclerActivated(ScreenObjEntry *screenObj);
void checkAllMotions();
void moveObj(ScreenObjEntry *screenObj);
void inDestination(ScreenObjEntry *screenObj);
@@ -924,10 +954,18 @@ public:
void nonBlockingText_IsShown();
void nonBlockingText_Forget();
- void nonBlockingText_CycleDone();
- void loadingTrigger_NewRoom(int16 newRoomNr);
- void loadingTrigger_DrawPicture();
+ void artificialDelay_Reset();
+ void artificialDelay_CycleDone();
+
+ uint16 artificialDelay_SearchTable(AgiArtificialDelayTriggerType triggerType, int16 orgNr, int16 newNr);
+
+ void artificialDelayTrigger_NewRoom(int16 newRoomNr);
+ void artificialDelayTrigger_DrawPicture(int16 newPictureNr);
+
+private:
+ int16 _artificialDelayCurrentRoom;
+ int16 _artificialDelayCurrentPicture;
public:
void redrawScreen();
diff --git a/engines/agi/appleIIgs_timedelay_overwrite.h b/engines/agi/appleIIgs_timedelay_overwrite.h
new file mode 100644
index 0000000000..c24d7cb5bd
--- /dev/null
+++ b/engines/agi/appleIIgs_timedelay_overwrite.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.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 AGI_APPLEIIGS_DELAY_OVERWRITE_H
+#define AGI_APPLEIIGS_DELAY_OVERWRITE_H
+
+namespace Agi {
+
+struct AgiAppleIIgsDelayOverwriteRoomEntry {
+ int16 fromRoom;
+ int16 toRoom;
+ int16 timeDelayOverwrite; // time delay here is like on PC, so 0 - unlimited, 1 - 20 cycles, 2 - 10 cycles
+ bool onlyWhenPlayerNotInControl;
+};
+
+struct AgiAppleIIgsDelayOverwriteGameEntry {
+ uint32 gameId;
+ int16 defaultTimeDelayOverwrite; // time delay here is like on PC, so 0 - unlimited, 1 - 20 cycles, 2 - 10 cycles
+ const AgiAppleIIgsDelayOverwriteRoomEntry *roomTable;
+};
+
+static const AgiAppleIIgsDelayOverwriteRoomEntry appleIIgsDelayOverwriteKQ4[] = {
+ { 120, 121, -1, true }, // Part of the intro: Graham gets his hat, throws it and breaks down, don't touch speed (3 is set)
+ { 128, 128, -1, true }, // Part of the intro: first actual room for gameplay, but during intro, don't touch speed (3 is set)
+ { 92, 92, -1, true }, // Part of caught by gargoyle w/ Lolotte cutscene (3 is set)
+ // room 54 sets the speed for a short time to 3 right after entering "clean" command. It doesn't seem to hurt that we switch it down to 2
+ // room 92 is dual use, part of cutscenes, part of gameplay, that's why we only stop touching it, when player is not in control
+ { 135, 135, -1, true }, // Part of ending cutscene. Don't touch speed (3 is set)
+ { -1, -1, -1, false }
+};
+
+static const AgiAppleIIgsDelayOverwriteRoomEntry appleIIgsDelayOverwriteMH1[] = {
+ //{ 153, 153, 2, false }, // Intro w/ credits
+ //{ 104, 104, 2, false }, // Intro cutscene
+ //{ 117, 117, 2, false }, // Intro cutscene (ego waking up)
+ { 114, 114, -1, false }, // interactive MAD map
+ { 124, 125, -1, false }, // MAD during intro (tracking), seem to work properly at given speed
+ { 132, 133, -1, false }, // MAD day 2 intro (tracking)
+ { 137, 137, -1, false }, // Night Club 4th arcade game - game sets speed to 7
+ { 115, 116, -1, false }, // MAD day 3 intro (tracking)
+ { 148, 148, -1, false }, // day 3: arcade sequence under pawn shop (game sets speed to 6)
+ { 103, 103, -1, false }, // MAD day 4 intro (tracking)
+ { 105, 105, -1, false }, // day 4 tracking mini game right at the start (game sets speed to 3)
+ { 107, 107, -1, false }, // MAD day 4 intro (tracking)
+ { 112, 112, -1, false }, // MAD day 4 intro (tracking)
+ { -1, -1, -1, false }
+};
+
+static const AgiAppleIIgsDelayOverwriteRoomEntry appleIIgsDelayOverwriteSQ2[] = {
+ { 1, 1, -1, false }, // Intro: space ship entering space port, don't touch speed
+ { -1, -1, -1, false }
+};
+
+static const AgiAppleIIgsDelayOverwriteGameEntry appleIIgsDelayOverwriteGameTable[] = {
+ { GID_BC, 2, nullptr }, // sets the speed at the start and doesn't modify it
+ { GID_GOLDRUSH, 2, nullptr },
+ { GID_KQ1, 2, nullptr },
+ // KQ2 seems to work fine at speed given by scripts
+ { GID_KQ3, 2, nullptr },
+ { GID_KQ4, 2, appleIIgsDelayOverwriteKQ4 },
+ { GID_LSL1, 2, nullptr }, // Switch Larry 1 to 10 cycles per second (that's around PC Larry 1's "normal" speed
+ { GID_MH1, 2, appleIIgsDelayOverwriteMH1 },
+ { GID_MIXEDUP, 2, nullptr },
+ { GID_PQ1, 2, nullptr },
+ { GID_SQ1, 2, nullptr }, // completed, no issues using these settings
+ { GID_SQ2, 2, appleIIgsDelayOverwriteSQ2 },
+ { GID_AGIDEMO, -1, nullptr }
+};
+
+} // End of namespace Agi
+
+#endif /* AGI_APPLEIIGS_DELAY_OVERWRITE_H */
diff --git a/engines/agi/checks.cpp b/engines/agi/checks.cpp
index 1e1670f674..c67b6a5810 100644
--- a/engines/agi/checks.cpp
+++ b/engines/agi/checks.cpp
@@ -129,7 +129,7 @@ bool AgiEngine::checkPriority(ScreenObjEntry *screenObj) {
screenPriority = _gfx->getPriority(curX, curY);
if (screenPriority == 0) { // unconditional black. no go at all!
- touchedControl = 0;
+ touchedControl = false;
break;
}
diff --git a/engines/agi/console.cpp b/engines/agi/console.cpp
index 6419e60219..9a4a357b44 100644
--- a/engines/agi/console.cpp
+++ b/engines/agi/console.cpp
@@ -33,27 +33,28 @@ namespace Agi {
Console::Console(AgiEngine *vm) : GUI::Debugger() {
_vm = vm;
- registerCmd("debug", WRAP_METHOD(Console, Cmd_Debug));
- registerCmd("cont", WRAP_METHOD(Console, Cmd_Cont));
- registerCmd("agiver", WRAP_METHOD(Console, Cmd_Agiver));
- registerCmd("version", WRAP_METHOD(Console, Cmd_Version));
- registerCmd("flags", WRAP_METHOD(Console, Cmd_Flags));
- registerCmd("logic0", WRAP_METHOD(Console, Cmd_Logic0));
- registerCmd("objs", WRAP_METHOD(Console, Cmd_Objs));
- registerCmd("runopcode", WRAP_METHOD(Console, Cmd_RunOpcode));
- registerCmd("opcode", WRAP_METHOD(Console, Cmd_Opcode));
- registerCmd("step", WRAP_METHOD(Console, Cmd_Step));
- registerCmd("trigger", WRAP_METHOD(Console, Cmd_Trigger));
- registerCmd("vars", WRAP_METHOD(Console, Cmd_Vars));
- registerCmd("setvar", WRAP_METHOD(Console, Cmd_SetVar));
- registerCmd("setflag", WRAP_METHOD(Console, Cmd_SetFlag));
- registerCmd("setobj", WRAP_METHOD(Console, Cmd_SetObj));
- registerCmd("room", WRAP_METHOD(Console, Cmd_Room));
- registerCmd("bt", WRAP_METHOD(Console, Cmd_BT));
- registerCmd("show_map", WRAP_METHOD(Console, Cmd_ShowMap));
- registerCmd("screenobj", WRAP_METHOD(Console, Cmd_ScreenObj));
- registerCmd("vmvars", WRAP_METHOD(Console, Cmd_VmVars));
- registerCmd("vmflags", WRAP_METHOD(Console, Cmd_VmFlags));
+ registerCmd("debug", WRAP_METHOD(Console, Cmd_Debug));
+ registerCmd("cont", WRAP_METHOD(Console, Cmd_Cont));
+ registerCmd("agiver", WRAP_METHOD(Console, Cmd_Agiver));
+ registerCmd("version", WRAP_METHOD(Console, Cmd_Version));
+ registerCmd("flags", WRAP_METHOD(Console, Cmd_Flags));
+ registerCmd("logic0", WRAP_METHOD(Console, Cmd_Logic0));
+ registerCmd("objs", WRAP_METHOD(Console, Cmd_Objs));
+ registerCmd("runopcode", WRAP_METHOD(Console, Cmd_RunOpcode));
+ registerCmd("opcode", WRAP_METHOD(Console, Cmd_Opcode));
+ registerCmd("step", WRAP_METHOD(Console, Cmd_Step));
+ registerCmd("trigger", WRAP_METHOD(Console, Cmd_Trigger));
+ registerCmd("vars", WRAP_METHOD(Console, Cmd_Vars));
+ registerCmd("setvar", WRAP_METHOD(Console, Cmd_SetVar));
+ registerCmd("setflag", WRAP_METHOD(Console, Cmd_SetFlag));
+ registerCmd("setobj", WRAP_METHOD(Console, Cmd_SetObj));
+ registerCmd("room", WRAP_METHOD(Console, Cmd_Room));
+ registerCmd("bt", WRAP_METHOD(Console, Cmd_BT));
+ registerCmd("show_map", WRAP_METHOD(Console, Cmd_ShowMap));
+ registerCmd("screenobj", WRAP_METHOD(Console, Cmd_ScreenObj));
+ registerCmd("vmvars", WRAP_METHOD(Console, Cmd_VmVars));
+ registerCmd("vmflags", WRAP_METHOD(Console, Cmd_VmFlags));
+ registerCmd("disableautosave", WRAP_METHOD(Console, Cmd_DisableAutomaticSave));
}
bool Console::Cmd_SetVar(int argc, const char **argv) {
@@ -609,6 +610,18 @@ bool Console::Cmd_VmFlags(int argc, const char **argv) {
return true;
}
+bool Console::Cmd_DisableAutomaticSave(int argc, const char **argv) {
+ if (!_vm->_game.automaticSave) {
+ debugPrintf("Automatic saving is currently not enabled\n");
+ return true;
+ }
+
+ _vm->_game.automaticSave = false;
+
+ debugPrintf("Automatic saving DISABLED!\n");
+ return true;
+}
+
bool Console::parseInteger(const char *argument, int &result) {
char *endPtr = 0;
int idxLen = strlen(argument);
diff --git a/engines/agi/console.h b/engines/agi/console.h
index 41dc9ddabc..ccc17b31de 100644
--- a/engines/agi/console.h
+++ b/engines/agi/console.h
@@ -66,6 +66,7 @@ private:
bool Cmd_ScreenObj(int argc, const char **argv);
bool Cmd_VmVars(int argc, const char **argv);
bool Cmd_VmFlags(int argc, const char **argv);
+ bool Cmd_DisableAutomaticSave(int argc, const char **argv);
bool parseInteger(const char *argument, int &result);
diff --git a/engines/agi/cycle.cpp b/engines/agi/cycle.cpp
index 17c9c873cc..19aca6f2c4 100644
--- a/engines/agi/cycle.cpp
+++ b/engines/agi/cycle.cpp
@@ -30,6 +30,7 @@
#include "agi/keyboard.h"
#include "agi/menu.h"
#include "agi/systemui.h"
+#include "agi/appleIIgs_timedelay_overwrite.h"
namespace Agi {
@@ -44,7 +45,7 @@ void AgiEngine::newRoom(int16 newRoomNr) {
int i;
// Loading trigger
- loadingTrigger_NewRoom(newRoomNr);
+ artificialDelayTrigger_NewRoom(newRoomNr);
debugC(4, kDebugLevelMain, "*** room %d ***", newRoomNr);
_sound->stopSound();
@@ -141,6 +142,9 @@ void AgiEngine::interpretCycle() {
oldScore = getVar(VM_VAR_SCORE);
oldSound = getFlag(VM_FLAG_SOUND_ON);
+ // Reset script heuristic here
+ resetGetVarSecondsHeuristic();
+
_game.exitAllLogics = false;
while (runLogic(0) == 0 && !(shouldQuit() || _restartGame)) {
setVar(VM_VAR_WORD_NOT_FOUND, 0);
@@ -149,10 +153,12 @@ void AgiEngine::interpretCycle() {
oldScore = getVar(VM_VAR_SCORE);
setFlag(VM_FLAG_ENTERED_CLI, false);
_game.exitAllLogics = false;
- nonBlockingText_CycleDone();
+ _veryFirstInitialCycle = false;
+ artificialDelay_CycleDone();
resetControllers();
}
- nonBlockingText_CycleDone();
+ _veryFirstInitialCycle = false;
+ artificialDelay_CycleDone();
resetControllers();
screenObjEgo->direction = getVar(VM_VAR_EGO_DIRECTION);
@@ -219,9 +225,10 @@ uint16 AgiEngine::processAGIEvents() {
// no inner loop active at the moment, regular processing
if (key) {
- setVar(VM_VAR_KEY, key & 0xFF);
if (!handleController(key)) {
if (key) {
+ // Only set VAR_KEY, when no controller/direction was detected
+ setVar(VM_VAR_KEY, key & 0xFF);
if (_text->promptIsEnabled()) {
_text->promptKeyPress(key);
}
@@ -288,6 +295,8 @@ uint16 AgiEngine::processAGIEvents() {
int AgiEngine::playGame() {
int ec = errOK;
+ const AgiAppleIIgsDelayOverwriteGameEntry *appleIIgsDelayOverwrite = nullptr;
+ const AgiAppleIIgsDelayOverwriteRoomEntry *appleIIgsDelayRoomOverwrite = nullptr;
debugC(2, kDebugLevelMain, "initializing...");
debugC(2, kDebugLevelMain, "game version = 0x%x", getVersion());
@@ -306,7 +315,7 @@ int AgiEngine::playGame() {
setFlag(VM_FLAG_LOGIC_ZERO_FIRST_TIME, true); // not in 2.917
setFlag(VM_FLAG_NEW_ROOM_EXEC, true); // needed for MUMG and SQ2!
setFlag(VM_FLAG_SOUND_ON, true); // enable sound
- setVar(VM_VAR_TIME_DELAY, 2); // "normal" speed
+ // do not set VM_VAR_TIME_DELAY, original AGI did not do it (in the data segment it was simply set to 0)
_game.gfxMode = true;
_text->promptRow_Set(22);
@@ -335,7 +344,17 @@ int AgiEngine::playGame() {
}
}
- nonBlockingText_Forget();
+ artificialDelay_Reset();
+
+ if (getPlatform() == Common::kPlatformApple2GS) {
+ // Look up, if there is a time delay overwrite table for the current game
+ appleIIgsDelayOverwrite = appleIIgsDelayOverwriteGameTable;
+ while (appleIIgsDelayOverwrite->gameId != GID_AGIDEMO) {
+ if (appleIIgsDelayOverwrite->gameId == getGameID())
+ break; // game found
+ appleIIgsDelayOverwrite++;
+ }
+ }
do {
processAGIEvents();
@@ -352,6 +371,46 @@ int AgiEngine::playGame() {
// Normally that game runs at TIME_DELAY 1.
// Maybe a script patch for this game would make sense.
// TODO: needs further investigation
+
+ int16 timeDelayOverwrite = -99;
+
+ // Now check, if we got a time delay overwrite entry for current room
+ if (appleIIgsDelayOverwrite->roomTable) {
+ byte curRoom = getVar(VM_VAR_CURRENT_ROOM);
+
+ appleIIgsDelayRoomOverwrite = appleIIgsDelayOverwrite->roomTable;
+ while (appleIIgsDelayRoomOverwrite->fromRoom >= 0) {
+ if ((appleIIgsDelayRoomOverwrite->fromRoom <= curRoom) && (appleIIgsDelayRoomOverwrite->toRoom >= curRoom)) {
+ if (appleIIgsDelayRoomOverwrite->onlyWhenPlayerNotInControl) {
+ if (_game.playerControl) {
+ // Player is actually currently in control? -> then skip this entry
+ appleIIgsDelayRoomOverwrite++;
+ continue;
+ }
+ }
+ timeDelayOverwrite = appleIIgsDelayRoomOverwrite->timeDelayOverwrite;
+ break;
+ }
+ appleIIgsDelayRoomOverwrite++;
+ }
+
+ if (timeDelayOverwrite == -99) {
+ // use default time delay in case no room specific one was found
+ timeDelayOverwrite = appleIIgsDelayOverwrite->defaultTimeDelayOverwrite;
+ }
+ } else {
+ timeDelayOverwrite = appleIIgsDelayOverwrite->defaultTimeDelayOverwrite;
+ }
+
+ if (timeDelayOverwrite >= 0) {
+ if (timeDelayOverwrite != timeDelay) {
+ // delayOverwrite is not the same as the delay taken from the scripts? overwrite it
+ //warning("AppleIIgs: time delay overwrite from %d to %d", timeDelay, timeDelayOverwrite);
+
+ setVar(VM_VAR_TIME_DELAY, timeDelayOverwrite - 1); // adjust for Apple IIgs
+ timeDelay = timeDelayOverwrite;
+ }
+ }
}
if (_passedPlayTimeCycles >= timeDelay) {
@@ -396,7 +455,7 @@ int AgiEngine::runGame() {
if (_restartGame) {
setFlag(VM_FLAG_RESTART_GAME, true);
- setVar(VM_VAR_TIME_DELAY, 2); // "normal" speed
+ // do not set VM_VAR_TIME_DELAY, original AGI did not do it
// Reset in-game timer
inGameTimerReset();
@@ -438,7 +497,13 @@ int AgiEngine::runGame() {
break;
case Common::kRenderHercA:
case Common::kRenderHercG:
- setVar(VM_VAR_MONITOR, kAgiMonitorHercules);
+ // Set EGA for now. Some games place text differently, when this is set to kAgiMonitorHercules.
+ // Text placement was different for Hercules rendering (16x12 instead of 16x16). There also was
+ // not enough space left for the prompt at the bottom. This was caused by the Hercules resolution.
+ // We don't have this restriction and we also support the regular prompt for Hercules mode.
+ // In theory Sierra could have had special Hercules code inside their games.
+ // TODO: check this.
+ setVar(VM_VAR_MONITOR, kAgiMonitorEga);
break;
// Don't know if Amiga AGI games use a different value than kAgiMonitorEga
// for vMonitor so I just use kAgiMonitorEga for them (As was done before too).
diff --git a/engines/agi/detection.cpp b/engines/agi/detection.cpp
index 6fca86db63..9f66d78d80 100644
--- a/engines/agi/detection.cpp
+++ b/engines/agi/detection.cpp
@@ -171,6 +171,26 @@ static const ADExtraGuiOptionsMap optionsList[] = {
}
},
+ {
+ GAMEOPTION_USE_HERCULES_FONT,
+ {
+ _s("Use Hercules hires font"),
+ _s("Uses Hercules hires font, when font file is available."),
+ "herculesfont",
+ false
+ }
+ },
+
+ {
+ GAMEOPTION_COMMAND_PROMPT_WINDOW,
+ {
+ _s("Pause when entering commands"),
+ _s("Shows a command prompt window and pauses the game (like in SCI) instead of a real-time prompt."),
+ "commandpromptwindow",
+ false
+ }
+ },
+
AD_EXTRA_GUI_OPTIONS_TERMINATOR
};
@@ -182,8 +202,8 @@ class AgiMetaEngine : public AdvancedMetaEngine {
public:
AgiMetaEngine() : AdvancedMetaEngine(Agi::gameDescriptions, sizeof(Agi::AGIGameDescription), agiGames, optionsList) {
- _singleid = "agi";
- _guioptions = GUIO1(GUIO_NOSPEECH);
+ _singleId = "agi";
+ _guiOptions = GUIO1(GUIO_NOSPEECH);
}
virtual const char *getName() const {
@@ -261,7 +281,6 @@ SaveStateList AgiMetaEngine::listSaves(const char *target) const {
pattern += ".###";
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) {
@@ -299,6 +318,8 @@ SaveStateList AgiMetaEngine::listSaves(const char *target) const {
}
}
+ // Sort saves based on slot number.
+ Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator());
return saveList;
}
@@ -359,6 +380,9 @@ SaveStateDescriptor AgiMetaEngine::querySaveMetaInfos(const char *target, int sl
uint32 saveDate = in->readUint32BE();
uint16 saveTime = in->readUint16BE();
+ if (saveVersion >= 9) {
+ in->readByte(); // skip over seconds of saveTime (not needed here)
+ }
if (saveVersion >= 6) {
uint32 playTime = in->readUint32BE();
descriptor.setPlayTime(playTime * 1000);
@@ -543,13 +567,13 @@ const ADGameDescription *AgiMetaEngine::fallbackDetect(const FileMap &allFilesXX
// Override the gameid & extra values in g_fallbackDesc.desc. This only works
// until the fallback detector is called again, and while the MetaEngine instance
// is alive (as else the string storage is modified/deleted).
- g_fallbackDesc.desc.gameid = _gameid.c_str();
+ g_fallbackDesc.desc.gameId = _gameid.c_str();
g_fallbackDesc.desc.extra = _extra.c_str();
Common::String fallbackWarning;
fallbackWarning = "Your game version has been detected using fallback matching as a\n";
- fallbackWarning += Common::String::format("variant of %s (%s).\n", g_fallbackDesc.desc.gameid, g_fallbackDesc.desc.extra);
+ fallbackWarning += Common::String::format("variant of %s (%s).\n", g_fallbackDesc.desc.gameId, g_fallbackDesc.desc.extra);
fallbackWarning += "If this is an original and unmodified version or new made Fanmade game,\n";
fallbackWarning += "please report any, information previously printed by ScummVM to the team.\n";
diff --git a/engines/agi/detection_tables.h b/engines/agi/detection_tables.h
index 8da255d9ee..0938e9d4d6 100644
--- a/engines/agi/detection_tables.h
+++ b/engines/agi/detection_tables.h
@@ -24,12 +24,14 @@ namespace Agi {
#define GAMEOPTION_ORIGINAL_SAVELOAD GUIO_GAMEOPTIONS1
#define GAMEOPTION_AMIGA_ALTERNATIVE_PALETTE GUIO_GAMEOPTIONS2
-#define GAMEOPTION_DISABLE_MOUSE GUIO_GAMEOPTIONS3
+#define GAMEOPTION_DISABLE_MOUSE GUIO_GAMEOPTIONS3
+#define GAMEOPTION_USE_HERCULES_FONT GUIO_GAMEOPTIONS4
+#define GAMEOPTION_COMMAND_PROMPT_WINDOW GUIO_GAMEOPTIONS5
// TODO: properly implement GAMEOPTIONs
-#define GAMEOPTIONS_DEFAULT GUIO2(GAMEOPTION_ORIGINAL_SAVELOAD,GAMEOPTION_DISABLE_MOUSE)
-#define GAMEOPTIONS_AMIGA GUIO2(GAMEOPTION_ORIGINAL_SAVELOAD,GAMEOPTION_AMIGA_ALTERNATIVE_PALETTE)
-#define GAMEOPTIONS_FANMADE_MOUSE GUIO1(GAMEOPTION_ORIGINAL_SAVELOAD)
+#define GAMEOPTIONS_DEFAULT GUIO4(GAMEOPTION_ORIGINAL_SAVELOAD,GAMEOPTION_DISABLE_MOUSE,GAMEOPTION_USE_HERCULES_FONT,GAMEOPTION_COMMAND_PROMPT_WINDOW)
+#define GAMEOPTIONS_AMIGA GUIO4(GAMEOPTION_ORIGINAL_SAVELOAD,GAMEOPTION_AMIGA_ALTERNATIVE_PALETTE,GAMEOPTION_USE_HERCULES_FONT,GAMEOPTION_COMMAND_PROMPT_WINDOW)
+#define GAMEOPTIONS_FANMADE_MOUSE GUIO3(GAMEOPTION_ORIGINAL_SAVELOAD,GAMEOPTION_USE_HERCULES_FONT,GAMEOPTION_COMMAND_PROMPT_WINDOW)
#define GAME_LVFPN(id,extra,fname,md5,size,lang,ver,features,gid,platform,interp,guioptions) { \
{ \
@@ -573,7 +575,14 @@ static const AGIGameDescription gameDescriptions[] = {
GAME("sq2", "2.0D 1988-03-14 3.5\"", "85390bde8958c39830e1adbe9fff87f3", 0x2936, GID_SQ2),
// Space Quest 2 (IIgs) 2.0A 7/25/88 (CE)
- GAME_P("sq2", "2.0A 1988-07-25 (CE)", "5dfdac98dd3c01fcfb166529f917e911", 0x2936, GID_SQ2, Common::kPlatformApple2GS),
+ // We have to see this as AGI < 2.936, because otherwise a set.pri.base call would somewhat break
+ // priority in SQ2, when entering Vohaul's vault.
+ // The Apple IIgs AGI included with SQ2 is the same as the one included with KQ3.
+ // We currently consider KQ3 IIgs to be a 2.917-equivalent.
+ // The SQ2 IIgs AGI definitely has 177 kernel functions, but it seems that Sierra shuffled the last few around / added a few extras at the end.
+ // For KQ3 set.pri.base is called with parameters that seem to be sound resources, which means
+ // set.pri.base was possibly discard.sound. For KQ4 onwards it seems this was cleaned up.
+ GAME_P("sq2", "2.0A 1988-07-25 (CE)", "5dfdac98dd3c01fcfb166529f917e911", 0x2917, GID_SQ2, Common::kPlatformApple2GS),
{
// Space Quest 2 (Amiga) 2.0F
@@ -849,6 +858,7 @@ static const AGIGameDescription gameDescriptions[] = {
FANMADE("Tex McPhilip 2 - Road To Divinity (v1.5)", "7387e8df854440bc26620ca0ea43af9a"),
FANMADE("Tex McPhilip 3 - A Destiny of Sin (Demo v0.25)", "992d12031a486ad84e592ff5d7c9d782"),
FANMADE("The 13th Disciple (v1.00)", "887719ad59afce9a41ec057dbb73ad73"),
+ FANMADE("The 13th Disciple (v1.01)", "58e3ec1b9ac1a79901c472aaa59db832"),
FANMADE("The Adventures of a Crazed Hermit", "6e3086cbb794d3299a9c5a9792295511"),
FANMADE("The Gourd of the Beans", "246f4d94946afb547482d44a53616d06"),
FANMADE("The Grateful Dead", "c2146631afacf8cb455ce24f3d2d46e7"),
diff --git a/engines/agi/font.cpp b/engines/agi/font.cpp
index 670c1bf575..f9605e4a3d 100644
--- a/engines/agi/font.cpp
+++ b/engines/agi/font.cpp
@@ -20,6 +20,7 @@
*
*/
+#include "common/config-manager.h"
#include "agi/agi.h"
#include "agi/font.h"
#include "agi/text.h"
@@ -31,6 +32,7 @@ GfxFont::GfxFont(AgiBase *vm) {
_fontData = nullptr;
_fontDataAllocated = nullptr;
+ _fontIsHires = false;
}
GfxFont::~GfxFont() {
@@ -619,52 +621,71 @@ static const uint8 fontData_ExtendedRussian[] = {
};
void GfxFont::init() {
- switch (_vm->_renderMode) {
- case Common::kRenderAmiga:
- // Try user-file first, if that fails use our internal inaccurate topaz font
- loadFontScummVMFile("agi-font-amiga.bin");
- if (!_fontData) {
- loadFontAmigaPseudoTopaz();
+ if (ConfMan.getBool("herculesfont")) {
+ // User wants, that we use Hercules hires font, try to load it
+ loadFontHercules();
+ } else {
+ switch (_vm->_renderMode) {
+ case Common::kRenderHercA:
+ case Common::kRenderHercG:
+ // Render mode is Hercules, we try to load Hercules hires font
+ loadFontHercules();
+ break;
+ default:
+ break;
}
- break;
- case Common::kRenderApple2GS:
- // Special font, stored in file AGIFONT
- loadFontAppleIIgs();
- break;
- case Common::kRenderAtariST:
- // TODO: Atari ST uses another font
- // Seems to be the standard Atari ST 8x8 system font
- loadFontScummVMFile("agi-font-atarist.bin");
- if (!_fontData) {
- loadFontAtariST("agi-font-atarist-system.fnt");
+ }
+
+ if (!_fontData) {
+ switch (_vm->_renderMode) {
+ case Common::kRenderAmiga:
+ // Try user-file first, if that fails use our internal inaccurate topaz font
+ loadFontScummVMFile("agi-font-amiga.bin");
if (!_fontData) {
- // TODO: in case we find a recreation of the font, add it in here
+ loadFontAmigaPseudoTopaz();
}
- }
- break;
- case Common::kRenderCGA:
- case Common::kRenderEGA:
- case Common::kRenderVGA:
- switch (_vm->getGameID()) {
- case GID_MICKEY:
- // load mickey mouse font from interpreter file
- loadFontMickey();
break;
+ case Common::kRenderApple2GS:
+ // Special font, stored in file AGIFONT
+ loadFontAppleIIgs();
+ break;
+ case Common::kRenderAtariST:
+ // TODO: Atari ST uses another font
+ // Seems to be the standard Atari ST 8x8 system font
+ loadFontScummVMFile("agi-font-atarist.bin");
+ if (!_fontData) {
+ loadFontAtariST("agi-font-atarist-system.fnt");
+ if (!_fontData) {
+ // TODO: in case we find a recreation of the font, add it in here
+ }
+ }
+ break;
+ case Common::kRenderHercA:
+ case Common::kRenderHercG:
+ case Common::kRenderCGA:
+ case Common::kRenderEGA:
+ case Common::kRenderVGA:
+ switch (_vm->getGameID()) {
+ case GID_MICKEY:
+ // load mickey mouse font from interpreter file
+ loadFontMickey();
+ break;
+ default:
+ loadFontScummVMFile("agi-font-dos.bin");
+ break;
+ }
+ break;
+
default:
- loadFontScummVMFile("agi-font-dos.bin");
break;
}
- break;
-
- default:
- break;
- }
- if (!_fontData) {
- // no font assigned?
- // use regular PC-BIOS font (taken from Dos-Box with a few modifications)
- _fontData = fontData_PCBIOS;
- debug("AGI: Using PC-BIOS font");
+ if (!_fontData) {
+ // no font assigned?
+ // use regular PC-BIOS font (taken from Dos-Box with a few modifications)
+ _fontData = fontData_PCBIOS;
+ debug("AGI: Using PC-BIOS font");
+ }
}
if (_vm->getLanguage() == Common::RU_RUS) {
@@ -678,6 +699,10 @@ const byte *GfxFont::getFontData() {
return _fontData;
}
+bool GfxFont::isFontHires() {
+ return _fontIsHires;
+}
+
void GfxFont::overwriteSaveRestoreDialogCharacter() {
// overwrite character 0x1A with the standard Sierra arrow to the right character
// required for the original save/restore dialogs
@@ -686,6 +711,11 @@ void GfxFont::overwriteSaveRestoreDialogCharacter() {
// Overwrite extended character set (0x80-0xFF) with Russian characters
void GfxFont::overwriteExtendedWithRussianSet() {
+ if (_fontIsHires) {
+ // TODO: Implement overwriting hires font characters too
+ return;
+ }
+
if (!_fontDataAllocated) {
// nothing allocated, we need to allocate space ourselves to be able to modify an internal font
_fontDataAllocated = (uint8 *)calloc(256, 8);
@@ -829,6 +859,11 @@ void GfxFont::loadFontAmigaPseudoTopaz() {
assert((topazBitOffset & 7) == 0);
topazByteOffset = topazBitOffset >> 3;
+
+ // Security check, although we are working on static const data from within ScummVM
+ uint maxOffset = (topazByteOffset + ((topazHeight - 1) * topazModulo));
+ assert(maxOffset < sizeof(fontData_AmigaPseudoTopaz));
+
for (uint16 curHeight = 0; curHeight < topazHeight; curHeight++) {
*fontData = topazData[topazByteOffset];
fontData++;
@@ -1160,4 +1195,102 @@ void GfxFont::loadFontAtariST(Common::String fontFilename) {
debug("AGI: Using Atari ST 8x8 system font");
}
+// Loads a Sierra Hercules font file
+void GfxFont::loadFontHercules() {
+ Common::File fontFile;
+ int32 fontFileSize = 0;
+ byte *fontData = nullptr;
+ byte *rawData = nullptr;
+
+ uint16 rawDataPos = 0;
+ uint16 curCharNr = 0;
+ uint16 curCharLine = 0;
+
+ if (fontFile.open("hgc_font")) {
+ // hgc_font file found, this is interleaved font data 16x12, should be 3072 bytes
+ // 24 bytes per character, 128 characters
+ fontFileSize = fontFile.size();
+ if (fontFileSize == (128 * 24)) {
+ // size seems to be fine
+ fontData = (uint8 *)calloc(256, 32);
+ _fontDataAllocated = fontData;
+
+ rawData = (byte *)calloc(128, 24);
+ fontFile.read(rawData, 128 * 24);
+
+ // convert interleaved 16x12 -> non-interleaved 16x16
+ for (curCharNr = 0; curCharNr < 128; curCharNr++) {
+ fontData += 4; // skip the first 2 lines
+ for (curCharLine = 0; curCharLine < 6; curCharLine++) {
+ fontData[0] = rawData[rawDataPos + 2 + 0];
+ fontData[1] = rawData[rawDataPos + 2 + 1];
+ fontData[2] = rawData[rawDataPos + 0 + 0];
+ fontData[3] = rawData[rawDataPos + 0 + 1];
+ rawDataPos += 4;
+ fontData += 4;
+ }
+ fontData += 4; // skip the last 2 lines
+ }
+
+ free(rawData);
+ } else {
+ warning("Fontfile 'hgc_font': unexpected file size");
+ }
+ fontFile.close();
+
+ }
+
+ // It seems hgc_graf.ovl holds a low-res font. It makes no real sense to use it.
+ // This was only done to AGI3 games and those rendered differently (2 pixel lines -> 3 pixel lines instead of 4)
+ // User could copy hgc_font from another AGI game over to get the hires font working.
+#if 0
+ if (!_fontDataAllocated) {
+ if (fontFile.open("hgc_graf.ovl")) {
+ // hgc_graf.ovl file found, this is font data + code. non-interleaved font data, should be 3075 bytes
+ // 16 bytes per character, 128 characters, 2048 bytes of font data, starting offset 21
+ fontFileSize = fontFile.size();
+ if (fontFileSize == 3075) {
+ // size seems to be fine
+ fontData = (uint8 *)calloc(256, 32);
+ _fontDataAllocated = fontData;
+
+ fontFile.seek(21);
+ rawData = (byte *)calloc(128, 16);
+ fontFile.read(rawData, 128 * 16);
+
+ // repeat every line 2 times to get 16x16 pixels
+ for (curCharNr = 0; curCharNr < 128; curCharNr++) {
+ for (curCharLine = 0; curCharLine < 8; curCharLine++) {
+ fontData[0] = rawData[rawDataPos + 0];
+ fontData[1] = rawData[rawDataPos + 1];
+ fontData[2] = rawData[rawDataPos + 0];
+ fontData[3] = rawData[rawDataPos + 1];
+ rawDataPos += 2;
+ fontData += 4;
+ }
+ }
+
+ free(rawData);
+
+ } else {
+ warning("Fontfile 'hgc_graf.ovl': unexpected file size");
+ }
+ fontFile.close();
+ }
+ }
+#endif
+
+ if (_fontDataAllocated) {
+ // font loaded
+ _fontData = _fontDataAllocated;
+ _fontIsHires = true;
+
+ debug("AGI: Using Hercules hires font");
+
+ } else {
+ // Continue, if no file was not found
+ warning("Could not open/use file 'hgc_font' for Hercules hires font");
+ }
+}
+
} // End of namespace Agi
diff --git a/engines/agi/font.h b/engines/agi/font.h
index 0bb1bbb18d..485b139858 100644
--- a/engines/agi/font.h
+++ b/engines/agi/font.h
@@ -36,6 +36,7 @@ private:
public:
void init();
const byte *getFontData();
+ bool isFontHires();
private:
void overwriteSaveRestoreDialogCharacter();
@@ -46,9 +47,11 @@ private:
void loadFontAmigaPseudoTopaz();
void loadFontAppleIIgs();
void loadFontAtariST(Common::String fontFilename);
+ void loadFontHercules();
const uint8 *_fontData; // pointer to the currently used font
uint8 *_fontDataAllocated;
+ bool _fontIsHires;
};
} // End of namespace Agi
diff --git a/engines/agi/global.cpp b/engines/agi/global.cpp
index 0f10976988..23256f27fb 100644
--- a/engines/agi/global.cpp
+++ b/engines/agi/global.cpp
@@ -23,6 +23,7 @@
#include "common/config-manager.h"
#include "agi/agi.h"
+#include "agi/graphics.h"
namespace Agi {
@@ -53,14 +54,21 @@ void AgiBase::flipFlag(int16 flagNr) {
void AgiEngine::setVar(int16 varNr, byte newValue) {
_game.vars[varNr] = newValue;
- if (varNr == VM_VAR_VOLUME) {
+ switch (varNr) {
+ case VM_VAR_SECONDS:
+ setVarSecondsTrigger(newValue);
+ break;
+ case VM_VAR_VOLUME:
setVolumeViaScripts(newValue);
+ break;
}
}
byte AgiEngine::getVar(int16 varNr) {
switch (varNr) {
case VM_VAR_SECONDS:
+ getVarSecondsHeuristicTrigger();
+ // is supposed to fall through
case VM_VAR_MINUTES:
case VM_VAR_HOURS:
case VM_VAR_DAYS:
@@ -79,7 +87,32 @@ byte AgiEngine::getVar(int16 varNr) {
// 15 - mute
void AgiEngine::setVolumeViaScripts(byte newVolume) {
newVolume = CLIP<byte>(newVolume, 0, 15);
- newVolume = 15 - newVolume; // turn volume around
+
+ if (_veryFirstInitialCycle) {
+ // WORKAROUND:
+ // The very first cycle is currently running and volume got changed
+ // This is surely the initial value. For plenty of fan games, a default of 15 is set
+ // Which actually means "mute" in AGI, but AGI on PC used PC speaker, which did not use
+ // volume setting. We do. So we detect such a situation and set a flag, so that the
+ // volume will get interpreted "correctly" for those fan games.
+ // Note: not all fan games are broken in that regard!
+ // See bug #7035
+ if (getFeatures() & GF_FANMADE) {
+ // We only check for fan games, Sierra always did it properly of course
+ if (newVolume == 15) {
+ // Volume gets set to mute at the start?
+ // Probably broken fan game detected, set flag
+ debug("Broken volume in fan game detected, enabling workaround");
+ _setVolumeBrokenFangame = true;
+ }
+ }
+ }
+
+ if (!_setVolumeBrokenFangame) {
+ // In AGI 15 is mute, 0 is loudest
+ // Some fan games set this incorrectly as 15 for loudest, 0 for mute
+ newVolume = 15 - newVolume; // turn volume around
+ }
int scummVMVolume = newVolume * Audio::Mixer::kMaxMixerVolume / 15;
bool scummVMMute = false;
@@ -135,10 +168,42 @@ void AgiEngine::setVolumeViaSystemSetting() {
_game.vars[VM_VAR_VOLUME] = internalVolume;
}
+void AgiEngine::resetGetVarSecondsHeuristic() {
+ _getVarSecondsHeuristicLastInstructionCounter = 0;
+ _getVarSecondsHeuristicCounter = 0;
+}
+
+// Called, when the scripts read VM_VAR_SECONDS
+void AgiEngine::getVarSecondsHeuristicTrigger() {
+ uint32 counterDifference = _instructionCounter - _getVarSecondsHeuristicLastInstructionCounter;
+
+ if (counterDifference <= 3) {
+ // Seconds were read within 3 instructions
+ _getVarSecondsHeuristicCounter++;
+ if (_getVarSecondsHeuristicCounter > 20) {
+ // More than 20 times in a row? This really seems to be an inner loop waiting for seconds to change
+ // This happens in at least:
+ // Police Quest 1 - Poker game (room 75, responsible script 81)
+
+ // Wait a few milliseconds, get events and update screen
+ // We MUST NOT process AGI events in here
+ wait(10);
+ processScummVMEvents();
+ _gfx->updateScreen();
+
+ _getVarSecondsHeuristicCounter = 0;
+ }
+ } else {
+ _getVarSecondsHeuristicCounter = 0;
+ }
+ _getVarSecondsHeuristicLastInstructionCounter = _instructionCounter;
+}
+
// In-Game timer, used for timer VM Variables
void AgiEngine::inGameTimerReset(uint32 newPlayTime) {
_lastUsedPlayTimeInCycles = newPlayTime / 50;
_lastUsedPlayTimeInSeconds = newPlayTime / 1000;
+ _playTimeInSecondsAdjust = 0; // no adjust for now
setTotalPlayTime(newPlayTime);
inGameTimerResetPassedCycles();
}
@@ -158,6 +223,24 @@ uint32 AgiEngine::inGameTimerGetPassedCycles() {
return _passedPlayTimeCycles;
}
+// Seconds got set by the game
+// This happens in Mixed Up Mother Goose. The game syncs the songs to VM_VAR_SECONDS, but instead
+// of only reading them, it sets it to 0 and then checks if it reached a certain second.
+// The original interpreter didn't reset the internal cycles counter. Which means the timing was never accurate,
+// because the cycles counter may just overflow right after setting the seconds, which means a second
+// increase almost immediately happened. We even fix this issue by adjusting for it.
+void AgiEngine::setVarSecondsTrigger(byte newSeconds) {
+ // Adjust in game timer, so that VM timer variables are accurate
+ inGameTimerUpdate();
+
+ // Adjust VM seconds again
+ _game.vars[VM_VAR_SECONDS] = newSeconds;
+
+ // Calculate milliseconds adjust (see comment above)
+ uint32 curPlayTimeMilliseconds = inGameTimerGet();
+ _playTimeInSecondsAdjust = curPlayTimeMilliseconds % 1000;
+}
+
// This is called, when one of the timer variables is read
// We calculate the latest variables, according to current official playtime
// This is also called in the main loop, because the game needs to be sync'd to 20 cycles per second
@@ -178,6 +261,14 @@ void AgiEngine::inGameTimerUpdate() {
_lastUsedPlayTimeInCycles = curPlayTimeCycles;
// Now calculate current play time in seconds
+ if (_playTimeInSecondsAdjust) {
+ // Apply adjust from setVarSecondsTrigger()
+ if (curPlayTimeMilliseconds >= _playTimeInSecondsAdjust) {
+ curPlayTimeMilliseconds -= _playTimeInSecondsAdjust;
+ } else {
+ curPlayTimeMilliseconds = 0;
+ }
+ }
uint32 curPlayTimeSeconds = curPlayTimeMilliseconds / 1000;
if (curPlayTimeSeconds == _lastUsedPlayTimeInSeconds) {
@@ -185,26 +276,50 @@ void AgiEngine::inGameTimerUpdate() {
return;
}
- uint32 secondsLeft = 0;
- byte curDays = 0;
- byte curHours = 0;
- byte curMinutes = 0;
- byte curSeconds = 0;
-
- curDays = curPlayTimeSeconds / 86400;
- secondsLeft = curPlayTimeSeconds % 86400;
-
- curHours = secondsLeft / 3600;
- secondsLeft = secondsLeft % 3600;
-
- curMinutes = secondsLeft / 60;
- curSeconds = secondsLeft % 60;
-
- // directly set them, otherwise we would go into an endless loop
- _game.vars[VM_VAR_SECONDS] = curSeconds;
- _game.vars[VM_VAR_MINUTES] = curMinutes;
- _game.vars[VM_VAR_HOURS] = curHours;
- _game.vars[VM_VAR_DAYS] = curDays;
+ int32 playTimeSecondsDelta = curPlayTimeSeconds - _lastUsedPlayTimeInSeconds;
+
+ if (playTimeSecondsDelta > 0) {
+ // Read and write to VM vars directly to avoid endless loop
+ uint32 secondsLeft = playTimeSecondsDelta;
+ byte curSeconds = _game.vars[VM_VAR_SECONDS];
+ byte curMinutes = _game.vars[VM_VAR_MINUTES];
+ byte curHours = _game.vars[VM_VAR_HOURS];
+ byte curDays = _game.vars[VM_VAR_DAYS];
+
+ // Add delta to VM variables
+ if (secondsLeft >= 86400) {
+ curDays += secondsLeft / 86400;
+ secondsLeft = secondsLeft % 86400;
+ }
+ if (secondsLeft >= 3600) {
+ curHours += secondsLeft / 3600;
+ secondsLeft = secondsLeft % 3600;
+ }
+ if (secondsLeft >= 60) {
+ curMinutes += secondsLeft / 60;
+ secondsLeft = secondsLeft % 60;
+ }
+ curSeconds += secondsLeft;
+
+ while (curSeconds > 59) {
+ curSeconds -= 60;
+ curMinutes++;
+ }
+ while (curMinutes > 59) {
+ curMinutes -= 60;
+ curHours++;
+ }
+ while (curHours > 23) {
+ curHours -= 24;
+ curDays++;
+ }
+
+ // directly set them
+ _game.vars[VM_VAR_SECONDS] = curSeconds;
+ _game.vars[VM_VAR_MINUTES] = curMinutes;
+ _game.vars[VM_VAR_HOURS] = curHours;
+ _game.vars[VM_VAR_DAYS] = curDays;
+ }
_lastUsedPlayTimeInSeconds = curPlayTimeSeconds;
}
diff --git a/engines/agi/graphics.cpp b/engines/agi/graphics.cpp
index ba5895ccd1..6d3563a451 100644
--- a/engines/agi/graphics.cpp
+++ b/engines/agi/graphics.cpp
@@ -39,7 +39,7 @@ namespace Agi {
#include "agi/font.h"
-GfxMgr::GfxMgr(AgiBase *vm) : _vm(vm) {
+GfxMgr::GfxMgr(AgiBase *vm, GfxFont *font) : _vm(vm), _font(font) {
_agipalFileNum = 0;
memset(&_paletteGfxMode, 0, sizeof(_paletteGfxMode));
@@ -50,7 +50,17 @@ GfxMgr::GfxMgr(AgiBase *vm) : _vm(vm) {
initPriorityTable();
- _renderStartOffsetY = 0;
+ _renderStartVisualOffsetY = 0;
+ _renderStartDisplayOffsetY = 0;
+
+ _upscaledHires = DISPLAY_UPSCALED_DISABLED;
+ _displayScreenWidth = DISPLAY_DEFAULT_WIDTH;
+ _displayScreenHeight = DISPLAY_DEFAULT_HEIGHT;
+ _displayFontWidth = 8;
+ _displayFontHeight = 8;
+
+ _displayWidthMulAdjust = 0; // visualPos * (2+0) = displayPos
+ _displayHeightMulAdjust = 0; // visualPos * (1+0) = displayPos
}
/**
@@ -59,6 +69,8 @@ GfxMgr::GfxMgr(AgiBase *vm) : _vm(vm) {
* @see deinit_video()
*/
int GfxMgr::initVideo() {
+ bool forceHires = false;
+
// Set up palettes
initPalette(_paletteTextMode, PALETTE_EGA);
@@ -72,6 +84,14 @@ int GfxMgr::initVideo() {
case Common::kRenderVGA:
initPalette(_paletteGfxMode, PALETTE_VGA, 256, 8);
break;
+ case Common::kRenderHercG:
+ initPalette(_paletteGfxMode, PALETTE_HERCULES_GREEN, 2, 8);
+ forceHires = true;
+ break;
+ case Common::kRenderHercA:
+ initPalette(_paletteGfxMode, PALETTE_HERCULES_AMBER, 2, 8);
+ forceHires = true;
+ break;
case Common::kRenderAmiga:
if (!ConfMan.getBool("altamigapalette")) {
// Set the correct Amiga palette depending on AGI interpreter version
@@ -87,7 +107,16 @@ int GfxMgr::initVideo() {
}
break;
case Common::kRenderApple2GS:
- initPalette(_paletteGfxMode, PALETTE_APPLE_II_GS, 16, 4);
+ switch (_vm->getGameID()) {
+ case GID_SQ1:
+ // Special one, only used for Space Quest 1 on Apple IIgs. Is the same as Amiga v1 palette
+ initPalette(_paletteGfxMode, PALETTE_APPLE_II_GS_SQ1, 16, 4);
+ break;
+ default:
+ // Regular "standard" Apple IIgs palette, used by everything else
+ initPalette(_paletteGfxMode, PALETTE_APPLE_II_GS, 16, 4);
+ break;
+ }
break;
case Common::kRenderAtariST:
initPalette(_paletteGfxMode, PALETTE_ATARI_ST, 16, 3);
@@ -116,31 +145,47 @@ int GfxMgr::initVideo() {
break;
}
+ //bool forcedUpscale = true;
+
+ if (_font->isFontHires() || forceHires) {
+ // Upscaling enable
+ _upscaledHires = DISPLAY_UPSCALED_640x400;
+ _displayScreenWidth = 640;
+ _displayScreenHeight = 400;
+ _displayFontWidth = 16;
+ _displayFontHeight = 16;
+
+ _displayWidthMulAdjust = 2;
+ _displayHeightMulAdjust = 1;
+ }
+
// set up mouse cursors
switch (_vm->_renderMode) {
case Common::kRenderEGA:
case Common::kRenderCGA:
case Common::kRenderVGA:
- initMouseCursor(&_mouseCursor, MOUSECURSOR_SCI, 11, 16, 1, 1);
+ case Common::kRenderHercG:
+ case Common::kRenderHercA:
+ initMouseCursor(&_mouseCursor, MOUSECURSOR_SCI, 11, 16, 0, 0);
initMouseCursor(&_mouseCursorBusy, MOUSECURSOR_SCI_BUSY, 15, 16, 7, 8);
break;
case Common::kRenderAmiga:
- initMouseCursor(&_mouseCursor, MOUSECURSOR_AMIGA, 8, 11, 1, 1);
+ initMouseCursor(&_mouseCursor, MOUSECURSOR_AMIGA, 8, 11, 0, 0);
initMouseCursor(&_mouseCursorBusy, MOUSECURSOR_AMIGA_BUSY, 13, 16, 7, 8);
break;
case Common::kRenderApple2GS:
// had no special busy mouse cursor
- initMouseCursor(&_mouseCursor, MOUSECURSOR_APPLE_II_GS, 9, 11, 1, 1);
+ initMouseCursor(&_mouseCursor, MOUSECURSOR_APPLE_II_GS, 9, 11, 0, 0);
initMouseCursor(&_mouseCursorBusy, MOUSECURSOR_SCI_BUSY, 15, 16, 7, 8);
break;
case Common::kRenderAtariST:
- initMouseCursor(&_mouseCursor, MOUSECURSOR_ATARI_ST, 11, 16, 1, 1);
+ initMouseCursor(&_mouseCursor, MOUSECURSOR_ATARI_ST, 11, 16, 0, 0);
initMouseCursor(&_mouseCursorBusy, MOUSECURSOR_SCI_BUSY, 15, 16, 7, 8);
break;
case Common::kRenderMacintosh:
// It looks like Atari ST + Macintosh used the same standard mouse cursor
// TODO: Verify by checking actual hardware
- initMouseCursor(&_mouseCursor, MOUSECURSOR_ATARI_ST, 11, 16, 1, 1);
+ initMouseCursor(&_mouseCursor, MOUSECURSOR_ATARI_ST, 11, 16, 0, 0);
initMouseCursor(&_mouseCursorBusy, MOUSECURSOR_MACINTOSH_BUSY, 10, 14, 7, 8);
break;
default:
@@ -149,15 +194,15 @@ int GfxMgr::initVideo() {
}
_pixels = SCRIPT_WIDTH * SCRIPT_HEIGHT;
- _visualScreen = (byte *)calloc(_pixels, 1);
+ _gameScreen = (byte *)calloc(_pixels, 1);
_priorityScreen = (byte *)calloc(_pixels, 1);
- _activeScreen = _visualScreen;
+ _activeScreen = _gameScreen;
//_activeScreen = _priorityScreen;
- _displayPixels = DISPLAY_WIDTH * DISPLAY_HEIGHT;
+ _displayPixels = _displayScreenWidth * _displayScreenHeight;
_displayScreen = (byte *)calloc(_displayPixels, 1);
- initGraphics(DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_WIDTH > 320);
+ initGraphics(_displayScreenWidth, _displayScreenHeight, _displayScreenWidth > 320);
setPalette(true); // set gfx-mode palette
@@ -174,27 +219,151 @@ int GfxMgr::initVideo() {
* @see init_video()
*/
int GfxMgr::deinitVideo() {
+ // Free mouse cursors in case they were allocated
+ if (_mouseCursor.bitmapDataAllocated)
+ free(_mouseCursor.bitmapDataAllocated);
+ if (_mouseCursorBusy.bitmapDataAllocated)
+ free(_mouseCursorBusy.bitmapDataAllocated);
+
free(_displayScreen);
- free(_visualScreen);
+ free(_gameScreen);
free(_priorityScreen);
return errOK;
}
void GfxMgr::setRenderStartOffset(uint16 offsetY) {
- if (offsetY >= (DISPLAY_HEIGHT - SCRIPT_HEIGHT))
+ if (offsetY >= (VISUAL_HEIGHT - SCRIPT_HEIGHT))
error("invalid render start offset");
- _renderStartOffsetY = offsetY;
+ _renderStartVisualOffsetY = offsetY;
+ _renderStartDisplayOffsetY = offsetY * (1 + _displayHeightMulAdjust);
+}
+uint16 GfxMgr::getRenderStartDisplayOffsetY() {
+ return _renderStartDisplayOffsetY;
+}
+
+// Translates a game screen coordinate to a display screen coordinate
+// Game screen to 320x200 -> x * 2, y + renderStart
+// Game screen to 640x400 -> x * 4, (y * 2) + renderStart
+void GfxMgr::translateGamePosToDisplayScreen(int16 &x, int16 &y) {
+ x = x * (2 + _displayWidthMulAdjust);
+ y = y * (1 + _displayHeightMulAdjust) + _renderStartDisplayOffsetY;
+}
+
+// Translates a visual coordinate to a display screen coordinate
+// Visual to 320x200 -> x * 2, y
+// Visual to 640x400 -> x * 4, y * 2
+void GfxMgr::translateVisualPosToDisplayScreen(int16 &x, int16 &y) {
+ x = x * (2 + _displayWidthMulAdjust);
+ y = y * (1 + _displayHeightMulAdjust);
+}
+
+// Translates a display screen coordinate to a game screen coordinate
+// Display screen to 320x200 -> x / 2, y - renderStart
+// Display screen to 640x400 -> x / 4, (y / 2) - renderStart
+void GfxMgr::translateDisplayPosToGameScreen(int16 &x, int16 &y) {
+ y -= _renderStartDisplayOffsetY; // remove status bar line
+ x = x / (2 + _displayWidthMulAdjust);
+ y = y / (1 + _displayHeightMulAdjust);
+ if (y < 0)
+ y = 0;
+ if (y >= SCRIPT_HEIGHT)
+ y = SCRIPT_HEIGHT + 1; // 1 beyond
+}
+
+// Translates dimension from visual screen to display screen
+void GfxMgr::translateVisualDimensionToDisplayScreen(int16 &width, int16 &height) {
+ width = width * (2 + _displayWidthMulAdjust);
+ height = height * (1 + _displayHeightMulAdjust);
+}
+
+// Translates dimension from display screen to visual screen
+void GfxMgr::translateDisplayDimensionToVisualScreen(int16 &width, int16 &height) {
+ width = width / (2 + _displayWidthMulAdjust);
+ height = height / (1 + _displayHeightMulAdjust);
+}
+
+// Translates a rect from game screen to display screen
+void GfxMgr::translateGameRectToDisplayScreen(int16 &x, int16 &y, int16 &width, int16 &height) {
+ translateGamePosToDisplayScreen(x, y);
+ translateVisualDimensionToDisplayScreen(width, height);
}
-uint16 GfxMgr::getRenderStartOffsetY() {
- return _renderStartOffsetY;
+
+// Translates a rect from visual screen to display screen
+void GfxMgr::translateVisualRectToDisplayScreen(int16 &x, int16 &y, int16 &width, int16 &height) {
+ translateVisualPosToDisplayScreen(x, y);
+ translateVisualDimensionToDisplayScreen(width, height);
+}
+
+uint32 GfxMgr::getDisplayOffsetToGameScreenPos(int16 x, int16 y) {
+ translateGamePosToDisplayScreen(x, y);
+ return (y * _displayScreenWidth) + x;
+}
+
+uint32 GfxMgr::getDisplayOffsetToVisualScreenPos(int16 x, int16 y) {
+ translateVisualPosToDisplayScreen(x, y);
+ return (y * _displayScreenWidth) + x;
+}
+
+// Attention: uses display screen coordinates!
+void GfxMgr::copyDisplayRectToScreen(int16 x, int16 y, int16 width, int16 height) {
+ g_system->copyRectToScreen(_displayScreen + y * _displayScreenWidth + x, _displayScreenWidth, x, y, width, height);
+}
+void GfxMgr::copyDisplayRectToScreen(int16 x, int16 adjX, int16 y, int16 adjY, int16 width, int16 adjWidth, int16 height, int16 adjHeight) {
+ switch (_upscaledHires) {
+ case DISPLAY_UPSCALED_DISABLED:
+ break;
+ case DISPLAY_UPSCALED_640x400:
+ adjX *= 2; adjY *= 2;
+ adjWidth *= 2; adjHeight *= 2;
+ break;
+ default:
+ assert(0);
+ break;
+ }
+ x += adjX; y += adjY;
+ width += adjWidth; height += adjHeight;
+ g_system->copyRectToScreen(_displayScreen + y * _displayScreenWidth + x, _displayScreenWidth, x, y, width, height);
+}
+void GfxMgr::copyDisplayRectToScreenUsingGamePos(int16 x, int16 y, int16 width, int16 height) {
+ translateGameRectToDisplayScreen(x, y, width, height);
+ g_system->copyRectToScreen(_displayScreen + (y * _displayScreenWidth) + x, _displayScreenWidth, x, y, width, height);
+}
+void GfxMgr::copyDisplayRectToScreenUsingVisualPos(int16 x, int16 y, int16 width, int16 height) {
+ translateVisualRectToDisplayScreen(x, y, width, height);
+ g_system->copyRectToScreen(_displayScreen + (y * _displayScreenWidth) + x, _displayScreenWidth, x, y, width, height);
+}
+void GfxMgr::copyDisplayToScreen() {
+ g_system->copyRectToScreen(_displayScreen, _displayScreenWidth, 0, 0, _displayScreenWidth, _displayScreenHeight);
+}
+
+void GfxMgr::translateFontPosToDisplayScreen(int16 &x, int16 &y) {
+ x *= _displayFontWidth;
+ y *= _displayFontHeight;
+}
+void GfxMgr::translateDisplayPosToFontScreen(int16 &x, int16 &y) {
+ x /= _displayFontWidth;
+ y /= _displayFontHeight;
+}
+void GfxMgr::translateFontDimensionToDisplayScreen(int16 &width, int16 &height) {
+ width *= _displayFontWidth;
+ height *= _displayFontHeight;
+}
+void GfxMgr::translateFontRectToDisplayScreen(int16 &x, int16 &y, int16 &width, int16 &height) {
+ translateFontPosToDisplayScreen(x, y);
+ translateFontDimensionToDisplayScreen(width, height);
+}
+Common::Rect GfxMgr::getFontRectForDisplayScreen(int16 column, int16 row, int16 width, int16 height) {
+ Common::Rect displayRect(width * _displayFontWidth, height * _displayFontHeight);
+ displayRect.moveTo(column * _displayFontWidth, row * _displayFontHeight);
+ return displayRect;
}
void GfxMgr::debugShowMap(int mapNr) {
switch (mapNr) {
case 0:
- _activeScreen = _visualScreen;
+ _activeScreen = _gameScreen;
break;
case 1:
_activeScreen = _priorityScreen;
@@ -203,11 +372,11 @@ void GfxMgr::debugShowMap(int mapNr) {
break;
}
- render_Block(0, 167, SCRIPT_WIDTH, SCRIPT_HEIGHT);
+ render_Block(0, 0, SCRIPT_WIDTH, SCRIPT_HEIGHT);
}
void GfxMgr::clear(byte color, byte priority) {
- memset(_visualScreen, color, _pixels);
+ memset(_gameScreen, color, _pixels);
memset(_priorityScreen, priority, _pixels);
}
@@ -215,7 +384,7 @@ void GfxMgr::clearDisplay(byte color, bool copyToScreen) {
memset(_displayScreen, color, _displayPixels);
if (copyToScreen) {
- g_system->copyRectToScreen(_displayScreen, DISPLAY_WIDTH, 0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT);
+ copyDisplayToScreen();
}
}
@@ -223,7 +392,7 @@ void GfxMgr::putPixel(int16 x, int16 y, byte drawMask, byte color, byte priority
int offset = y * SCRIPT_WIDTH + x;
if (drawMask & GFX_SCREEN_MASK_VISUAL) {
- _visualScreen[offset] = color;
+ _gameScreen[offset] = color;
}
if (drawMask & GFX_SCREEN_MASK_PRIORITY) {
_priorityScreen[offset] = priority;
@@ -231,15 +400,72 @@ void GfxMgr::putPixel(int16 x, int16 y, byte drawMask, byte color, byte priority
}
void GfxMgr::putPixelOnDisplay(int16 x, int16 y, byte color) {
- int offset = y * DISPLAY_WIDTH + x;
+ uint32 offset = 0;
+
+ switch (_upscaledHires) {
+ case DISPLAY_UPSCALED_DISABLED:
+ offset = y * _displayScreenWidth + x;
+
+ _displayScreen[offset] = color;
+ break;
+ case DISPLAY_UPSCALED_640x400:
+ offset = (y * _displayScreenWidth) + x;
- _displayScreen[offset] = color;
+ _displayScreen[offset + 0] = color;
+ _displayScreen[offset + 1] = color;
+ _displayScreen[offset + _displayScreenWidth + 0] = color;
+ _displayScreen[offset + _displayScreenWidth + 1] = color;
+ break;
+ default:
+ break;
+ }
+}
+
+void GfxMgr::putPixelOnDisplay(int16 x, int16 adjX, int16 y, int16 adjY, byte color) {
+ switch (_upscaledHires) {
+ case DISPLAY_UPSCALED_DISABLED:
+ break;
+ case DISPLAY_UPSCALED_640x400:
+ adjX *= 2; adjY *= 2;
+ break;
+ default:
+ assert(0);
+ break;
+ }
+ x += adjX;
+ y += adjY;
+ putPixelOnDisplay(x, y, color);
+}
+
+void GfxMgr::putFontPixelOnDisplay(int16 baseX, int16 baseY, int16 addX, int16 addY, byte color, bool isHires) {
+ uint32 offset = 0;
+
+ switch (_upscaledHires) {
+ case DISPLAY_UPSCALED_DISABLED:
+ offset = ((baseY + addY) * _displayScreenWidth) + (baseX + addX);
+ _displayScreen[offset] = color;
+ break;
+ case DISPLAY_UPSCALED_640x400:
+ if (isHires) {
+ offset = ((baseY + addY) * _displayScreenWidth) + (baseX + addX);
+ _displayScreen[offset] = color;
+ } else {
+ offset = ((baseY + addY * 2) * _displayScreenWidth) + (baseX + addX * 2);
+ _displayScreen[offset + 0] = color;
+ _displayScreen[offset + 1] = color;
+ _displayScreen[offset + _displayScreenWidth + 0] = color;
+ _displayScreen[offset + _displayScreenWidth + 1] = color;
+ }
+ break;
+ default:
+ break;
+ }
}
byte GfxMgr::getColor(int16 x, int16 y) {
int offset = y * SCRIPT_WIDTH + x;
- return _visualScreen[offset];
+ return _gameScreen[offset];
}
byte GfxMgr::getPriority(int16 x, int16 y) {
@@ -279,12 +505,17 @@ byte GfxMgr::getCGAMixtureColor(byte color) {
return CGA_MixtureColorTable[color & 0x0F];
}
-// Attention: y-coordinate points to the LOWER left!
+// Attention: in our implementation, y-coordinate is upper left.
+// Sierra passed the lower left instead. We changed it to make upscaling easier.
void GfxMgr::render_Block(int16 x, int16 y, int16 width, int16 height, bool copyToScreen) {
if (!render_Clip(x, y, width, height))
return;
switch (_vm->_renderMode) {
+ case Common::kRenderHercG:
+ case Common::kRenderHercA:
+ render_BlockHercules(x, y, width, height, copyToScreen);
+ break;
case Common::kRenderCGA:
render_BlockCGA(x, y, width, height, copyToScreen);
break;
@@ -295,17 +526,26 @@ void GfxMgr::render_Block(int16 x, int16 y, int16 width, int16 height, bool copy
}
if (copyToScreen) {
- int16 upperY = y - height + 1 + _renderStartOffsetY;
- g_system->copyRectToScreen(_displayScreen + upperY * DISPLAY_WIDTH + x * 2, DISPLAY_WIDTH, x * 2, upperY, width * 2, height);
+ copyDisplayRectToScreenUsingGamePos(x, y, width, height);
}
}
bool GfxMgr::render_Clip(int16 &x, int16 &y, int16 &width, int16 &height, int16 clipAgainstWidth, int16 clipAgainstHeight) {
if ((x >= clipAgainstWidth) || ((x + width - 1) < 0) ||
- (y < 0) || ((y - (height - 1)) >= clipAgainstHeight)) {
+ (y < 0) || ((y + (height - 1)) >= clipAgainstHeight)) {
return false;
}
+ if (y < 0) {
+ height += y;
+ y = 0;
+ }
+
+ if ((y + height - 1) >= clipAgainstHeight) {
+ height = clipAgainstHeight - y;
+ }
+
+#if 0
if ((y - height + 1) < 0)
height = y + 1;
@@ -313,6 +553,7 @@ bool GfxMgr::render_Clip(int16 &x, int16 &y, int16 &width, int16 &height, int16
height -= y - (clipAgainstHeight - 1);
y = clipAgainstHeight - 1;
}
+#endif
if (x < 0) {
width += x;
@@ -326,52 +567,212 @@ bool GfxMgr::render_Clip(int16 &x, int16 &y, int16 &width, int16 &height, int16
}
void GfxMgr::render_BlockEGA(int16 x, int16 y, int16 width, int16 height, bool copyToScreen) {
- int offsetVisual = SCRIPT_WIDTH * y + x;
- int offsetDisplay = (DISPLAY_WIDTH * (y + _renderStartOffsetY)) + x * 2;
+ uint32 offsetVisual = SCRIPT_WIDTH * y + x;
+ uint32 offsetDisplay = getDisplayOffsetToGameScreenPos(x, y);
int16 remainingWidth = width;
int16 remainingHeight = height;
byte curColor = 0;
+ int16 displayWidth = width * (2 + _displayWidthMulAdjust);
while (remainingHeight) {
remainingWidth = width;
- while (remainingWidth) {
- curColor = _activeScreen[offsetVisual++];
- _displayScreen[offsetDisplay++] = curColor;
- _displayScreen[offsetDisplay++] = curColor;
- remainingWidth--;
+
+ switch (_upscaledHires) {
+ case DISPLAY_UPSCALED_DISABLED:
+ while (remainingWidth) {
+ curColor = _activeScreen[offsetVisual++];
+ _displayScreen[offsetDisplay++] = curColor;
+ _displayScreen[offsetDisplay++] = curColor;
+ remainingWidth--;
+ }
+ break;
+ case DISPLAY_UPSCALED_640x400:
+ while (remainingWidth) {
+ curColor = _activeScreen[offsetVisual++];
+ memset(&_displayScreen[offsetDisplay], curColor, 4);
+ memset(&_displayScreen[offsetDisplay + _displayScreenWidth], curColor, 4);
+ offsetDisplay += 4;
+ remainingWidth--;
+ }
+ break;
+ default:
+ assert(0);
+ break;
+ }
+
+ offsetVisual += SCRIPT_WIDTH - width;
+ offsetDisplay += _displayScreenWidth - displayWidth;
+
+ switch (_upscaledHires) {
+ case DISPLAY_UPSCALED_640x400:
+ offsetDisplay += _displayScreenWidth;;
+ break;
+ default:
+ break;
}
- offsetVisual -= SCRIPT_WIDTH + width;
- offsetDisplay -= DISPLAY_WIDTH + width * 2;
remainingHeight--;
}
}
void GfxMgr::render_BlockCGA(int16 x, int16 y, int16 width, int16 height, bool copyToScreen) {
- int offsetVisual = SCRIPT_WIDTH * y + x;
- int offsetDisplay = (DISPLAY_WIDTH * (y + _renderStartOffsetY)) + x * 2;
+ uint32 offsetVisual = SCRIPT_WIDTH * y + x;
+ uint32 offsetDisplay = getDisplayOffsetToGameScreenPos(x, y);
+ int16 remainingWidth = width;
+ int16 remainingHeight = height;
+ byte curColor = 0;
+ int16 displayWidth = width * (2 + _displayWidthMulAdjust);
+
+ while (remainingHeight) {
+ remainingWidth = width;
+
+ switch (_upscaledHires) {
+ case DISPLAY_UPSCALED_DISABLED:
+ while (remainingWidth) {
+ curColor = _activeScreen[offsetVisual++];
+ _displayScreen[offsetDisplay++] = curColor & 0x03; // we process CGA mixture
+ _displayScreen[offsetDisplay++] = curColor >> 2;
+ remainingWidth--;
+ }
+ break;
+ case DISPLAY_UPSCALED_640x400:
+ while (remainingWidth) {
+ curColor = _activeScreen[offsetVisual++];
+ _displayScreen[offsetDisplay + 0] = curColor & 0x03; // we process CGA mixture
+ _displayScreen[offsetDisplay + 1] = curColor >> 2;
+ _displayScreen[offsetDisplay + 2] = curColor & 0x03;
+ _displayScreen[offsetDisplay + 3] = curColor >> 2;
+ _displayScreen[offsetDisplay + _displayScreenWidth + 0] = curColor & 0x03;
+ _displayScreen[offsetDisplay + _displayScreenWidth + 1] = curColor >> 2;
+ _displayScreen[offsetDisplay + _displayScreenWidth + 2] = curColor & 0x03;
+ _displayScreen[offsetDisplay + _displayScreenWidth + 3] = curColor >> 2;
+ offsetDisplay += 4;
+ remainingWidth--;
+ }
+ break;
+ default:
+ assert(0);
+ break;
+ }
+
+ offsetVisual += SCRIPT_WIDTH - width;
+ offsetDisplay += _displayScreenWidth - displayWidth;
+
+ switch (_upscaledHires) {
+ case DISPLAY_UPSCALED_640x400:
+ offsetDisplay += _displayScreenWidth;;
+ break;
+ default:
+ break;
+ }
+
+ remainingHeight--;
+ }
+}
+
+static const uint8 herculesColorMapping[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x88, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00,
+ 0x80, 0x10, 0x02, 0x20, 0x01, 0x08, 0x40, 0x04,
+ 0xAA, 0x00, 0xAA, 0x00, 0xAA, 0x00, 0xAA, 0x00,
+ 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88,
+ 0x88, 0x00, 0x88, 0x00, 0x88, 0x00, 0x88, 0x00,
+ 0x11, 0x22, 0x44, 0x88, 0x11, 0x22, 0x44, 0x88,
+ 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA,
+ 0x22, 0x00, 0x88, 0x00, 0x22, 0x00, 0x88, 0x00,
+ 0xD7, 0xFF, 0x7D, 0xFF, 0xD7, 0xFF, 0x7D, 0xFF,
+ 0xDD, 0x55, 0x77, 0xAA, 0xDD, 0x55, 0x77, 0xAA,
+ 0x7F, 0xEF, 0xFD, 0xDF, 0xFE, 0xF7, 0xBF, 0xFB,
+ 0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF,
+ 0x77, 0xBB, 0xDD, 0xEE, 0x77, 0xBB, 0xDD, 0xEE,
+ 0x77, 0xFF, 0xFF, 0xFF, 0xDD, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+// Sierra actually seems to have rendered the whole screen all the time
+void GfxMgr::render_BlockHercules(int16 x, int16 y, int16 width, int16 height, bool copyToScreen) {
+ uint32 offsetVisual = SCRIPT_WIDTH * y + x;
+ uint32 offsetDisplay = getDisplayOffsetToGameScreenPos(x, y);
int16 remainingWidth = width;
int16 remainingHeight = height;
byte curColor = 0;
+ int16 displayWidth = width * (2 + _displayWidthMulAdjust);
+
+ assert(_upscaledHires == DISPLAY_UPSCALED_640x400);
+
+ uint16 lookupOffset1 = (y * 2 & 0x07);
+ uint16 lookupOffset2 = 0;
+ bool getUpperNibble = false;
+ byte herculesColors1 = 0;
+ byte herculesColors2 = 0;
while (remainingHeight) {
remainingWidth = width;
+
+ lookupOffset1 = (lookupOffset1 + 0) & 0x07;
+ lookupOffset2 = (lookupOffset1 + 1) & 0x07;
+
+ getUpperNibble = (x & 1) ? false : true;
while (remainingWidth) {
- curColor = _activeScreen[offsetVisual++];
- _displayScreen[offsetDisplay++] = curColor & 0x03; // we process CGA mixture
- _displayScreen[offsetDisplay++] = curColor >> 2;
+ curColor = _activeScreen[offsetVisual++] & 0x0F;
+
+ if (getUpperNibble) {
+ herculesColors1 = herculesColorMapping[curColor * 8 + lookupOffset1] & 0x0F;
+ herculesColors2 = herculesColorMapping[curColor * 8 + lookupOffset2] & 0x0F;
+ } else {
+ herculesColors1 = herculesColorMapping[curColor * 8 + lookupOffset1] >> 4;
+ herculesColors2 = herculesColorMapping[curColor * 8 + lookupOffset2] >> 4;
+ }
+ getUpperNibble ^= true;
+
+ _displayScreen[offsetDisplay + 0] = (herculesColors1 & 0x08) ? 1 : 0;
+ _displayScreen[offsetDisplay + 1] = (herculesColors1 & 0x04) ? 1 : 0;
+ _displayScreen[offsetDisplay + 2] = (herculesColors1 & 0x02) ? 1 : 0;
+ _displayScreen[offsetDisplay + 3] = (herculesColors1 & 0x01) ? 1 : 0;
+
+ _displayScreen[offsetDisplay + _displayScreenWidth + 0] = (herculesColors2 & 0x08) ? 1 : 0;
+ _displayScreen[offsetDisplay + _displayScreenWidth + 1] = (herculesColors2 & 0x04) ? 1 : 0;
+ _displayScreen[offsetDisplay + _displayScreenWidth + 2] = (herculesColors2 & 0x02) ? 1 : 0;
+ _displayScreen[offsetDisplay + _displayScreenWidth + 3] = (herculesColors2 & 0x01) ? 1 : 0;
+
+ offsetDisplay += 4;
remainingWidth--;
}
- offsetVisual -= SCRIPT_WIDTH + width;
- offsetDisplay -= DISPLAY_WIDTH + width * 2;
+
+ lookupOffset1 += 2;
+
+ offsetVisual += SCRIPT_WIDTH - width;
+ offsetDisplay += _displayScreenWidth - displayWidth;
+ offsetDisplay += _displayScreenWidth;;
remainingHeight--;
}
}
+// Table used for at least Manhunter 2, it renders 2 lines -> 3 lines instead of 4
+// Manhunter 1 is shipped with a broken Hercules font
+// King's Quest 4 aborts right at the start, when Hercules rendering is active
+#if 0
+static const uint8 herculesCoordinateOffset[] = {
+ 0x00, 0x01, 0x03, 0x04, 0x06, 0x07, 0x01, 0x02,
+ 0x04, 0x05, 0x07, 0x00, 0x02, 0x03, 0x05, 0x06
+};
+
+static const uint8 herculesColorMapping[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x02, 0x00, 0x40, 0x00, 0x08, 0x00,
+ 0x80, 0x10, 0x02, 0x20, 0x01, 0x08, 0x40, 0x04, 0xAA, 0x00, 0xAA, 0x00, 0xAA, 0x00, 0xAA, 0x00,
+ 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x88, 0x00, 0x88, 0x00, 0x88, 0x00, 0x88, 0x00,
+ 0x11, 0x22, 0x44, 0x88, 0x11, 0x22, 0x44, 0x88, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA,
+ 0x22, 0x00, 0x88, 0x00, 0x22, 0x00, 0x88, 0x00, 0xD7, 0xFF, 0x7D, 0xFF, 0xD7, 0xFF, 0x7D, 0xFF,
+ 0xDD, 0x55, 0x77, 0xAA, 0xDD, 0x55, 0x77, 0xAA, 0x7F, 0xEF, 0xFD, 0xDF, 0xFE, 0xF7, 0xBF, 0xFB,
+ 0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0x77, 0xBB, 0xDD, 0xEE, 0x77, 0xBB, 0xDD, 0xEE,
+ 0x7F, 0xEF, 0xFB, 0xBF, 0xEF, 0xFE, 0xBF, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+#endif
+
void GfxMgr::transition_Amiga() {
uint16 screenPos = 1;
- uint16 screenStepPos = 1;
+ uint32 screenStepPos = 1;
int16 posY = 0, posX = 0;
int16 stepCount = 0;
@@ -393,15 +794,29 @@ void GfxMgr::transition_Amiga() {
posY = screenStepPos / SCRIPT_WIDTH;
posX = screenStepPos - (posY * SCRIPT_WIDTH);
- posY += _renderStartOffsetY; // adjust to only update the main area, not the status bar
- posX *= 2; // adjust for display screen
-
- screenStepPos = (screenStepPos * 2) + (_renderStartOffsetY * DISPLAY_WIDTH); // adjust here too for display screen
- for (int16 multiPixel = 0; multiPixel < 4; multiPixel++) {
- g_system->copyRectToScreen(_displayScreen + screenStepPos, DISPLAY_WIDTH, posX, posY, 2, 1);
- screenStepPos += (0x1A40 * 2); // 6720d
- posY += 42;
+ // Adjust to only update the game screen, not the status bar
+ translateGamePosToDisplayScreen(posX, posY);
+
+ switch (_upscaledHires) {
+ case DISPLAY_UPSCALED_DISABLED:
+ for (int16 multiPixel = 0; multiPixel < 4; multiPixel++) {
+ screenStepPos = (posY * _displayScreenWidth) + posX;
+ g_system->copyRectToScreen(_displayScreen + screenStepPos, _displayScreenWidth, posX, posY, 2, 1);
+ posY += 42;
+ }
+ break;
+ case DISPLAY_UPSCALED_640x400:
+ for (int16 multiPixel = 0; multiPixel < 4; multiPixel++) {
+ screenStepPos = (posY * _displayScreenWidth) + posX;
+ g_system->copyRectToScreen(_displayScreen + screenStepPos, _displayScreenWidth, posX, posY, 4, 2);
+ posY += 42 * 2;
+ }
+ break;
+ default:
+ assert(0);
+ break;
}
+
stepCount++;
if (stepCount == 220) {
// 30 times for the whole transition, so should take around 0.5 seconds
@@ -424,7 +839,7 @@ void GfxMgr::transition_Amiga() {
// Atari ST definitely had a hi-res transition using the full resolution unlike the Amiga transition.
void GfxMgr::transition_AtariSt() {
uint16 screenPos = 1;
- uint16 screenStepPos = 1;
+ uint32 screenStepPos = 1;
int16 posY = 0, posX = 0;
int16 stepCount = 0;
@@ -443,17 +858,31 @@ void GfxMgr::transition_AtariSt() {
if ((screenPos < 13440) && (screenPos & 1)) {
screenStepPos = screenPos >> 1;
- posY = screenStepPos / DISPLAY_WIDTH;
- posX = screenStepPos - (posY * DISPLAY_WIDTH);
-
- posY += _renderStartOffsetY; // adjust to only update the main area, not the status bar
-
- screenStepPos = screenStepPos + (_renderStartOffsetY * DISPLAY_WIDTH); // adjust here too for display screen
- for (int16 multiPixel = 0; multiPixel < 8; multiPixel++) {
- g_system->copyRectToScreen(_displayScreen + screenStepPos, DISPLAY_WIDTH, posX, posY, 1, 1);
- screenStepPos += 0x1a40; // 6720d
- posY += 21;
+ posY = screenStepPos / DISPLAY_DEFAULT_WIDTH;
+ posX = screenStepPos - (posY * DISPLAY_DEFAULT_WIDTH);
+
+ switch (_upscaledHires) {
+ case DISPLAY_UPSCALED_DISABLED:
+ posY += _renderStartDisplayOffsetY; // adjust to only update the main area, not the status bar
+ for (int16 multiPixel = 0; multiPixel < 8; multiPixel++) {
+ screenStepPos = (posY * _displayScreenWidth) + posX;
+ g_system->copyRectToScreen(_displayScreen + screenStepPos, _displayScreenWidth, posX, posY, 1, 1);
+ posY += 21;
+ }
+ break;
+ case DISPLAY_UPSCALED_640x400:
+ posX *= 2; posY *= 2;
+ posY += _renderStartDisplayOffsetY; // adjust to only update the main area, not the status bar
+ for (int16 multiPixel = 0; multiPixel < 8; multiPixel++) {
+ screenStepPos = (posY * _displayScreenWidth) + posX;
+ g_system->copyRectToScreen(_displayScreen + screenStepPos, _displayScreenWidth, posX, posY, 2, 2);
+ posY += 21 * 2;
+ }
+ break;
+ default:
+ break;
}
+
stepCount++;
if (stepCount == 168) {
// 40 times for the whole transition, so should take around 0.7 seconds
@@ -484,7 +913,7 @@ void GfxMgr::block_save(int16 x, int16 y, int16 width, int16 height, byte *buffe
//warning("block_save: %d, %d -> %d, %d", x, y, width, height);
while (remainingHeight) {
- memcpy(curBufferPtr, _visualScreen + offset, width);
+ memcpy(curBufferPtr, _gameScreen + offset, width);
offset += SCRIPT_WIDTH;
curBufferPtr += width;
remainingHeight--;
@@ -510,7 +939,7 @@ void GfxMgr::block_restore(int16 x, int16 y, int16 width, int16 height, byte *bu
//warning("block_restore: %d, %d -> %d, %d", x, y, width, height);
while (remainingHeight) {
- memcpy(_visualScreen + offset, curBufferPtr, width);
+ memcpy(_gameScreen + offset, curBufferPtr, width);
offset += SCRIPT_WIDTH;
curBufferPtr += width;
remainingHeight--;
@@ -526,12 +955,8 @@ void GfxMgr::block_restore(int16 x, int16 y, int16 width, int16 height, byte *bu
}
}
-// Attention: uses visual screen coordinates!
-void GfxMgr::copyDisplayRectToScreen(int16 x, int16 y, int16 width, int16 height) {
- g_system->copyRectToScreen(_displayScreen + y * DISPLAY_WIDTH + x, DISPLAY_WIDTH, x, y, width, height);
-}
-
// coordinates are for visual screen, but are supposed to point somewhere inside the playscreen
+// x, y is the upper left. Sierra passed them as lower left. We change that to make upscaling easier.
// attention: Clipping is done here against 160x200 instead of 160x168
// Original interpreter didn't do any clipping, we do it for security.
// Clipping against the regular script width/height must not be done,
@@ -539,13 +964,13 @@ void GfxMgr::copyDisplayRectToScreen(int16 x, int16 y, int16 width, int16 height
// Going beyond 160x168 will result in messageboxes not getting fully removed
// In KQ4's case, the scripts clear the screen that's why it works.
void GfxMgr::drawBox(int16 x, int16 y, int16 width, int16 height, byte backgroundColor, byte lineColor) {
- if (!render_Clip(x, y, width, height, SCRIPT_WIDTH, DISPLAY_HEIGHT - _renderStartOffsetY))
+ if (!render_Clip(x, y, width, height, VISUAL_WIDTH, VISUAL_HEIGHT - _renderStartVisualOffsetY))
return;
// coordinate translation: visual-screen -> display-screen
- x = x * 2;
- y = y + _renderStartOffsetY; // drawDisplayRect paints anywhere on the whole screen, our coordinate is within playscreen
- width = width * 2; // width was given as visual width, we need display width
+ translateVisualRectToDisplayScreen(x, y, width, height);
+
+ y = y + _renderStartDisplayOffsetY; // drawDisplayRect paints anywhere on the whole screen, our coordinate is within playscreen
// draw box background
drawDisplayRect(x, y, width, height, backgroundColor);
@@ -555,75 +980,87 @@ void GfxMgr::drawBox(int16 x, int16 y, int16 width, int16 height, byte backgroun
case Common::kRenderApple2GS:
case Common::kRenderAmiga:
// Slightly different window frame, and actually using 1-pixel width, which is "hi-res"
- drawDisplayRect(x + 2, y - 2, width - 4, 1, lineColor);
- drawDisplayRect(x + width - 3, y - 2, 1, height - 4, lineColor);
- drawDisplayRect(x + 2, y - height + 3, width - 4, 1, lineColor);
- drawDisplayRect(x + 2, y - 2, 1, height - 4, lineColor);
+ drawDisplayRect(x, +2, y, +2, width, -4, 0, 1, lineColor);
+ drawDisplayRect(x + width, -3, y, +2, 0, 1, height, -4, lineColor);
+ drawDisplayRect(x, +2, y + height, -3, width, -4, 0, 1, lineColor);
+ drawDisplayRect(x, +2, y, +2, 0, 1, height, -4, lineColor);
break;
case Common::kRenderMacintosh:
// 1 pixel between box and frame lines. Frame lines were black
- drawDisplayRect(x + 1, y - 1, width - 2, 1, 0);
- drawDisplayRect(x + width - 2, y - 1, 1, height - 2, 0);
- drawDisplayRect(x + 1, y - height + 2, width - 2, 1, 0);
- drawDisplayRect(x + 1, y - 1, 1, height - 2, 0);
+ drawDisplayRect(x, +1, y, +1, width, -2, 0, 1, 0);
+ drawDisplayRect(x + width, -2, y, +1, 0, 1, height, -2, 0);
+ drawDisplayRect(x, +1, y + height, -2, width, -2, 0, 1, 0);
+ drawDisplayRect(x, +1, y, +1, 0, 1, height, -2, 0);
break;
+ case Common::kRenderHercA:
+ case Common::kRenderHercG:
+ lineColor = 0; // change linecolor to black
+ // supposed to fall through
case Common::kRenderCGA:
case Common::kRenderEGA:
case Common::kRenderVGA:
case Common::kRenderAtariST:
default:
- drawDisplayRect(x + 2, y - 1, width - 4, 1, lineColor);
- drawDisplayRect(x + width - 4, y - 2, 2, height - 4, lineColor);
- drawDisplayRect(x + 2, y - height + 2, width - 4, 1, lineColor);
- drawDisplayRect(x + 2, y - 2, 2, height - 4, lineColor);
+ drawDisplayRect(x, +2, y, +1, width, -4, 0, 1, lineColor);
+ drawDisplayRect(x + width, -4, y, +2, 0, 2, height, -4, lineColor);
+ drawDisplayRect(x, +2, y + height, -2, width, -4, 0, 1, lineColor);
+ drawDisplayRect(x, +2, y, +2, 0, 2, height, -4, lineColor);
break;
}
}
-// coordinates for visual screen
-void GfxMgr::drawRect(int16 x, int16 y, int16 width, int16 height, byte color) {
- if (!render_Clip(x, y, width, height, SCRIPT_WIDTH, DISPLAY_HEIGHT - _renderStartOffsetY))
- return;
-
- // coordinate translation: visual-screen -> display-screen
- x = x * 2;
- y = y + _renderStartOffsetY; // drawDisplayRect paints anywhere on the whole screen, our coordinate is within playscreen
- width = width * 2; // width was given as visual width, we need display width
-
- drawDisplayRect(x, y, width, height, color);
-}
-
// coordinates are directly for display screen
void GfxMgr::drawDisplayRect(int16 x, int16 y, int16 width, int16 height, byte color, bool copyToScreen) {
switch (_vm->_renderMode) {
case Common::kRenderCGA:
drawDisplayRectCGA(x, y, width, height, color);
break;
+ case Common::kRenderHercG:
+ case Common::kRenderHercA:
+ if (color)
+ color = 1; // change any color except black to green/amber
+ // supposed to fall through
case Common::kRenderEGA:
default:
drawDisplayRectEGA(x, y, width, height, color);
break;
}
if (copyToScreen) {
- int16 upperY = y - height + 1;
- g_system->copyRectToScreen(_displayScreen + upperY * DISPLAY_WIDTH + x, DISPLAY_WIDTH, x, upperY, width, height);
+ copyDisplayRectToScreen(x, y, width, height);
}
}
+void GfxMgr::drawDisplayRect(int16 x, int16 adjX, int16 y, int16 adjY, int16 width, int16 adjWidth, int16 height, int16 adjHeight, byte color, bool copyToScreen) {
+ switch (_upscaledHires) {
+ case DISPLAY_UPSCALED_DISABLED:
+ x += adjX; y += adjY;
+ width += adjWidth; height += adjHeight;
+ break;
+ case DISPLAY_UPSCALED_640x400:
+ x += adjX * 2; y += adjY * 2;
+ width += adjWidth * 2; height += adjHeight * 2;
+ break;
+ default:
+ assert(0);
+ break;
+ }
+ drawDisplayRect(x, y, width, height, color, copyToScreen);
+}
+
void GfxMgr::drawDisplayRectEGA(int16 x, int16 y, int16 width, int16 height, byte color) {
- int offsetDisplay = (DISPLAY_WIDTH * y) + x;
+ uint32 offsetDisplay = (y * _displayScreenWidth) + x;
int16 remainingHeight = height;
while (remainingHeight) {
memset(_displayScreen + offsetDisplay, color, width);
- offsetDisplay -= DISPLAY_WIDTH;
+ offsetDisplay += _displayScreenWidth;
remainingHeight--;
}
}
void GfxMgr::drawDisplayRectCGA(int16 x, int16 y, int16 width, int16 height, byte color) {
- int offsetDisplay = (DISPLAY_WIDTH * y) + x;
+ uint32 offsetDisplay = (y * _displayScreenWidth) + x;
int16 remainingHeight = height;
int16 remainingWidth = width;
byte CGAMixtureColor = getCGAMixtureColor(color);
@@ -644,18 +1081,20 @@ void GfxMgr::drawDisplayRectCGA(int16 x, int16 y, int16 width, int16 height, byt
remainingWidth -= 2;
}
- offsetDisplay -= DISPLAY_WIDTH;
+ offsetDisplay += _displayScreenWidth;
remainingHeight--;
}
}
// row + column are text-coordinates
void GfxMgr::drawCharacter(int16 row, int16 column, byte character, byte foreground, byte background, bool disabledLook) {
- int16 x = column * FONT_DISPLAY_WIDTH;
- int16 y = row * FONT_DISPLAY_HEIGHT;
+ int16 x = column;
+ int16 y = row;
byte transformXOR = 0;
byte transformOR = 0;
+ translateFontPosToDisplayScreen(x, y);
+
// Now figure out, if special handling needs to be done
if (_vm->_game.gfxMode) {
if (background & 0x08) {
@@ -676,22 +1115,43 @@ void GfxMgr::drawStringOnDisplay(int16 x, int16 y, const char *text, byte foregr
while (*text) {
drawCharacterOnDisplay(x, y, *text, foregroundColor, backgroundColor);
text++;
- x += FONT_DISPLAY_WIDTH;
+ x += _displayFontWidth;
+ }
+}
+
+void GfxMgr::drawStringOnDisplay(int16 x, int16 adjX, int16 y, int16 adjY, const char *text, byte foregroundColor, byte backgroundColor) {
+ switch (_upscaledHires) {
+ case DISPLAY_UPSCALED_DISABLED:
+ x += adjX;
+ y += adjY;
+ break;
+ case DISPLAY_UPSCALED_640x400:
+ x += adjX * 2;
+ y += adjY * 2;
+ break;
+ default:
+ assert(0);
+ break;
}
+ drawStringOnDisplay(x, y, text, foregroundColor, backgroundColor);
}
void GfxMgr::drawCharacterOnDisplay(int16 x, int16 y, const byte character, byte foreground, byte background, byte transformXOR, byte transformOR) {
int16 curX, curY;
const byte *fontData;
+ bool fontIsHires = _font->isFontHires();
+ int16 fontHeight = fontIsHires ? 16 : FONT_DISPLAY_HEIGHT;
+ int16 fontWidth = fontIsHires ? 16 : FONT_DISPLAY_WIDTH;
+ int16 fontBytesPerCharacter = fontIsHires ? 32 : FONT_BYTES_PER_CHARACTER;
byte curByte = 0;
uint16 curBit;
// get font data of specified character
- fontData = _vm->getFontData() + character * FONT_BYTES_PER_CHARACTER;
+ fontData = _font->getFontData() + character * fontBytesPerCharacter;
curBit = 0;
- for (curY = 0; curY < FONT_DISPLAY_HEIGHT; curY++) {
- for (curX = 0; curX < FONT_DISPLAY_WIDTH; curX++) {
+ for (curY = 0; curY < fontHeight; curY++) {
+ for (curX = 0; curX < fontWidth; curX++) {
if (!curBit) {
curByte = *fontData;
// do transformations in case they are needed (invert/disabled look)
@@ -701,9 +1161,9 @@ void GfxMgr::drawCharacterOnDisplay(int16 x, int16 y, const byte character, byte
curBit = 0x80;
}
if (curByte & curBit) {
- putPixelOnDisplay(x + curX, y + curY, foreground);
+ putFontPixelOnDisplay(x, y, curX, curY, foreground, fontIsHires);
} else {
- putPixelOnDisplay(x + curX, y + curY, background);
+ putFontPixelOnDisplay(x, y, curX, curY, background, fontIsHires);
}
curBit = curBit >> 1;
}
@@ -711,18 +1171,20 @@ void GfxMgr::drawCharacterOnDisplay(int16 x, int16 y, const byte character, byte
transformOR ^= 0xFF;
}
- copyDisplayRectToScreen(x, y, FONT_DISPLAY_WIDTH, FONT_DISPLAY_HEIGHT);
+ copyDisplayRectToScreen(x, y, _displayFontWidth, _displayFontHeight);
}
#define SHAKE_VERTICAL_PIXELS 4
-#define SHAKE_HORIZONTAL_PIXELS 8
+#define SHAKE_HORIZONTAL_PIXELS 4
// Sierra used some EGA port trickery to do it, we have to do it by copying pixels around
void GfxMgr::shakeScreen(int16 repeatCount) {
int shakeNr, shakeCount;
uint8 *blackSpace;
+ int16 shakeHorizontalPixels = SHAKE_HORIZONTAL_PIXELS * (2 + _displayWidthMulAdjust);
+ int16 shakeVerticalPixels = SHAKE_VERTICAL_PIXELS * (1 + _displayHeightMulAdjust);
- if ((blackSpace = (uint8 *)calloc(SHAKE_HORIZONTAL_PIXELS * DISPLAY_WIDTH, 1)) == NULL)
+ if ((blackSpace = (uint8 *)calloc(shakeVerticalPixels * _displayScreenWidth, 1)) == NULL)
return;
shakeCount = repeatCount * 8; // effectively 4 shakes per repeat
@@ -732,12 +1194,12 @@ void GfxMgr::shakeScreen(int16 repeatCount) {
for (shakeNr = 0; shakeNr < shakeCount; shakeNr++) {
if (shakeNr & 1) {
// move back
- copyDisplayRectToScreen(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT);
+ copyDisplayToScreen();
} else {
- g_system->copyRectToScreen(_displayScreen, DISPLAY_WIDTH, SHAKE_HORIZONTAL_PIXELS, SHAKE_VERTICAL_PIXELS, DISPLAY_WIDTH - SHAKE_HORIZONTAL_PIXELS, DISPLAY_HEIGHT - SHAKE_VERTICAL_PIXELS);
+ g_system->copyRectToScreen(_displayScreen, _displayScreenWidth, shakeHorizontalPixels, shakeVerticalPixels, _displayScreenWidth - shakeHorizontalPixels, _displayScreenHeight - shakeVerticalPixels);
// additionally fill the remaining space with black
- g_system->copyRectToScreen(blackSpace, DISPLAY_WIDTH, 0, 0, DISPLAY_WIDTH, SHAKE_VERTICAL_PIXELS);
- g_system->copyRectToScreen(blackSpace, SHAKE_HORIZONTAL_PIXELS, 0, 0, SHAKE_HORIZONTAL_PIXELS, DISPLAY_HEIGHT);
+ g_system->copyRectToScreen(blackSpace, _displayScreenWidth, 0, 0, _displayScreenWidth, shakeVerticalPixels);
+ g_system->copyRectToScreen(blackSpace, shakeHorizontalPixels, 0, 0, shakeHorizontalPixels, _displayScreenHeight);
}
g_system->updateScreen();
g_system->delayMillis(66); // Sierra waited for 4 V'Syncs, which is around 66 milliseconds
@@ -751,13 +1213,18 @@ void GfxMgr::updateScreen() {
}
void GfxMgr::initPriorityTable() {
+ _priorityTableSet = false;
+
+ createDefaultPriorityTable(_priorityTable);
+}
+
+void GfxMgr::createDefaultPriorityTable(uint8 *priorityTable) {
int16 priority, step;
int16 yPos = 0;
- _priorityTableSet = false;
for (priority = 1; priority < 15; priority++) {
for (step = 0; step < 12; step++) {
- _priorityTable[yPos++] = priority < 4 ? 4 : priority;
+ priorityTable[yPos++] = priority < 4 ? 4 : priority;
}
}
}
@@ -776,11 +1243,35 @@ void GfxMgr::setPriorityTable(int16 priorityBase) {
}
}
+// used for saving
+int16 GfxMgr::saveLoadGetPriority(int16 yPos) {
+ assert(yPos < SCRIPT_HEIGHT);
+ return _priorityTable[yPos];
+}
+bool GfxMgr::saveLoadWasPriorityTableModified() {
+ return _priorityTableSet;
+}
+
// used for restoring
-void GfxMgr::setPriority(int16 yPos, int16 priority) {
+void GfxMgr::saveLoadSetPriority(int16 yPos, int16 priority) {
assert(yPos < SCRIPT_HEIGHT);
_priorityTable[yPos] = priority;
}
+void GfxMgr::saveLoadSetPriorityTableModifiedBool(bool wasModified) {
+ _priorityTableSet = wasModified;
+}
+void GfxMgr::saveLoadFigureOutPriorityTableModifiedBool() {
+ uint8 defaultPriorityTable[SCRIPT_HEIGHT]; /**< priority table */
+
+ createDefaultPriorityTable(defaultPriorityTable);
+
+ if (memcmp(defaultPriorityTable, _priorityTable, sizeof(_priorityTable)) == 0) {
+ // Match, it is the default table, so reset the flag
+ _priorityTableSet = false;
+ } else {
+ _priorityTableSet = true;
+ }
+}
/**
* Convert sprite priority to y value.
@@ -793,7 +1284,8 @@ int16 GfxMgr::priorityToY(int16 priority) {
return (priority - 5) * 12 + 48;
}
- // dynamic priority bands were introduced in 3.002.086 (effectively AGI3)
+ // Dynamic priority bands were introduced in 2.425, but removed again until 2.936 (effectively last version of AGI2)
+ // They are available from 2.936 onwards.
// It seems there was a glitch, that caused priority bands to not get calculated properly.
// It was caused by this function starting with Y = 168 instead of 167, which meant it always
// returned with 168 as result.
@@ -804,6 +1296,8 @@ int16 GfxMgr::priorityToY(int16 priority) {
// drawn first, followed by ego, which would then draw ego over the dwarf.
// For more information see bug #1712585 (dwarf sprite priority)
//
+ // This glitch is definitely present in 2.425, 2.936 and 3.002.086.
+ //
// Priority bands were working properly in: 3.001.098 (Black Cauldron)
uint16 agiVersion = _vm->getVersion();
@@ -925,7 +1419,38 @@ int GfxMgr::getAGIPalFileNum() {
}
void GfxMgr::initMouseCursor(MouseCursorData *mouseCursor, const byte *bitmapData, uint16 width, uint16 height, int hotspotX, int hotspotY) {
- mouseCursor->bitmapData = bitmapData;
+ switch (_upscaledHires) {
+ case DISPLAY_UPSCALED_DISABLED:
+ mouseCursor->bitmapData = bitmapData;
+ break;
+ case DISPLAY_UPSCALED_640x400: {
+ mouseCursor->bitmapDataAllocated = (byte *)malloc(width * height * 4);
+ mouseCursor->bitmapData = mouseCursor->bitmapDataAllocated;
+
+ // Upscale mouse cursor
+ byte *upscaledData = mouseCursor->bitmapDataAllocated;
+
+ for (uint16 y = 0; y < height; y++) {
+ for (uint16 x = 0; x < width; x++) {
+ byte curColor = *bitmapData++;
+ upscaledData[x * 2 + 0] = curColor;
+ upscaledData[x * 2 + 1] = curColor;
+ upscaledData[x * 2 + (width * 2) + 0] = curColor;
+ upscaledData[x * 2 + (width * 2) + 1] = curColor;
+ }
+ upscaledData += width * 2 * 2;
+ }
+
+ width *= 2;
+ height *= 2;
+ hotspotX *= 2;
+ hotspotY *= 2;
+ break;
+ }
+ default:
+ assert(0);
+ break;
+ }
mouseCursor->width = width;
mouseCursor->height = height;
mouseCursor->hotspotX = hotspotX;
diff --git a/engines/agi/graphics.h b/engines/agi/graphics.h
index fb596626c6..1cb595cdfa 100644
--- a/engines/agi/graphics.h
+++ b/engines/agi/graphics.h
@@ -29,13 +29,15 @@ namespace Agi {
#define SCRIPT_WIDTH 160
#define SCRIPT_HEIGHT 168
-#define DISPLAY_WIDTH 320
-#define DISPLAY_HEIGHT 200
-
-#define GFX_WIDTH 320
-#define GFX_HEIGHT 200
-#define CHAR_COLS 8
-#define CHAR_LINES 8
+#define VISUAL_WIDTH 160
+#define VISUAL_HEIGHT 200
+#define DISPLAY_DEFAULT_WIDTH 320
+#define DISPLAY_DEFAULT_HEIGHT 200
+
+enum GfxScreenUpscaledMode {
+ DISPLAY_UPSCALED_DISABLED = 0,
+ DISPLAY_UPSCALED_640x400 = 1
+};
class AgiEngine;
@@ -47,6 +49,7 @@ enum GfxScreenMasks {
struct MouseCursorData {
const byte *bitmapData;
+ byte *bitmapDataAllocated;
uint16 width;
uint16 height;
int hotspotX;
@@ -56,6 +59,7 @@ struct MouseCursorData {
class GfxMgr {
private:
AgiBase *_vm;
+ GfxFont *_font;
uint8 _paletteGfxMode[256 * 3];
uint8 _paletteTextMode[256 * 3];
@@ -64,7 +68,7 @@ private:
int _agipalFileNum;
public:
- GfxMgr(AgiBase *vm);
+ GfxMgr(AgiBase *vm, GfxFont *font);
int initVideo();
int deinitVideo();
@@ -78,18 +82,58 @@ public:
void setMouseCursor(bool busy = false);
void setRenderStartOffset(uint16 offsetY);
- uint16 getRenderStartOffsetY();
+ uint16 getRenderStartDisplayOffsetY();
+
+ void translateGamePosToDisplayScreen(int16 &x, int16 &y);
+ void translateVisualPosToDisplayScreen(int16 &x, int16 &y);
+ void translateDisplayPosToGameScreen(int16 &x, int16 &y);
+
+ void translateVisualDimensionToDisplayScreen(int16 &width, int16 &height);
+ void translateDisplayDimensionToVisualScreen(int16 &width, int16 &height);
+
+ void translateGameRectToDisplayScreen(int16 &x, int16 &y, int16 &width, int16 &height);
+ void translateVisualRectToDisplayScreen(int16 &x, int16 &y, int16 &width, int16 &height);
+ void translateDisplayRectToVisualScreen(int16 &x, int16 &y, int16 &width, int16 &height);
+
+ uint32 getDisplayOffsetToGameScreenPos(int16 x, int16 y);
+ uint32 getDisplayOffsetToVisualScreenPos(int16 x, int16 y);
+
+ void copyDisplayRectToScreen(int16 x, int16 y, int16 width, int16 height);
+ void copyDisplayRectToScreen(int16 x, int16 adjX, int16 y, int16 adjY, int16 width, int16 adjWidth, int16 height, int16 adjHeight);
+ void copyDisplayRectToScreenUsingGamePos(int16 x, int16 y, int16 width, int16 height);
+ void copyDisplayRectToScreenUsingVisualPos(int16 x, int16 y, int16 width, int16 height);
+ void copyDisplayToScreen();
+
+ void translateFontPosToDisplayScreen(int16 &x, int16 &y);
+ void translateDisplayPosToFontScreen(int16 &x, int16 &y);
+ void translateFontDimensionToDisplayScreen(int16 &width, int16 &height);
+ void translateFontRectToDisplayScreen(int16 &x, int16 &y, int16 &width, int16 &height);
+ Common::Rect getFontRectForDisplayScreen(int16 column, int16 row, int16 width, int16 height);
private:
uint _pixels;
- //uint16 _displayWidth;
- //uint16 _displayHeight;
uint _displayPixels;
byte *_activeScreen;
- byte *_visualScreen; // 160x168
- byte *_priorityScreen; // 160x168
- byte *_displayScreen; // 320x200
+ byte *_gameScreen; // 160x168 - screen, where the actual game content is drawn to (actual graphics, not including status line, prompt, etc.)
+ byte *_priorityScreen; // 160x168 - screen contains priority information of the game screen
+ // the term "visual screen" is effectively the display screen, but at 160x200 resolution. Used for coordinate translation
+ byte *_displayScreen; // 320x200 or 640x400 - screen, that the game is rendered to and which is then copied to framebuffer
+
+ uint16 _displayScreenWidth;
+ uint16 _displayScreenHeight;
+
+ uint16 _displayFontWidth;
+ uint16 _displayFontHeight;
+
+ uint16 _displayWidthMulAdjust;
+ uint16 _displayHeightMulAdjust;
+
+ /**
+ * This variable defines, if upscaled hires is active and what upscaled mode
+ * is used.
+ */
+ GfxScreenUpscaledMode _upscaledHires;
bool _priorityTableSet;
uint8 _priorityTable[SCRIPT_HEIGHT]; /**< priority table */
@@ -97,15 +141,32 @@ private:
MouseCursorData _mouseCursor;
MouseCursorData _mouseCursorBusy;
- uint16 _renderStartOffsetY;
+ uint16 _renderStartVisualOffsetY;
+ uint16 _renderStartDisplayOffsetY;
public:
+ uint16 getDisplayScreenWidth() {
+ return _displayScreenWidth;
+ }
+ uint16 getDisplayFontWidth() {
+ return _displayFontWidth;
+ }
+ uint16 getDisplayFontHeight() {
+ return _displayFontHeight;
+ }
+
+ GfxScreenUpscaledMode getUpscaledHires() {
+ return _upscaledHires;
+ }
+
void debugShowMap(int mapNr);
void clear(byte color, byte priority);
void clearDisplay(byte color, bool copyToScreen = true);
void putPixel(int16 x, int16 y, byte drawMask, byte color, byte priority);
void putPixelOnDisplay(int16 x, int16 y, byte color);
+ void putPixelOnDisplay(int16 x, int16 adjX, int16 y, int16 adjY, byte color);
+ void putFontPixelOnDisplay(int16 baseX, int16 baseY, int16 addX, int16 addY, byte color, bool isHires);
byte getColor(int16 x, int16 y);
byte getPriority(int16 x, int16 y);
@@ -119,6 +180,7 @@ public:
private:
void render_BlockEGA(int16 x, int16 y, int16 width, int16 height, bool copyToScreen);
void render_BlockCGA(int16 x, int16 y, int16 width, int16 height, bool copyToScreen);
+ void render_BlockHercules(int16 x, int16 y, int16 width, int16 height, bool copyToScreen);
public:
void transition_Amiga();
@@ -127,11 +189,9 @@ public:
void block_save(int16 x, int16 y, int16 width, int16 height, byte *bufferPtr);
void block_restore(int16 x, int16 y, int16 width, int16 height, byte *bufferPtr);
- void copyDisplayRectToScreen(int16 x, int16 y, int16 width, int16 height);
-
void drawBox(int16 x, int16 y, int16 width, int16 height, byte backgroundColor, byte lineColor);
- void drawRect(int16 x, int16 y, int16 width, int16 height, byte color);
void drawDisplayRect(int16 x, int16 y, int16 width, int16 height, byte color, bool copyToScreen = true);
+ void drawDisplayRect(int16 x, int16 adjX, int16 y, int16 adjY, int16 width, int16 adjWidth, int16 height, int16 adjHeight, byte color, bool copyToScreen = true);
private:
void drawDisplayRectEGA(int16 x, int16 y, int16 width, int16 height, byte color);
void drawDisplayRectCGA(int16 x, int16 y, int16 width, int16 height, byte color);
@@ -139,14 +199,21 @@ private:
public:
void drawCharacter(int16 row, int16 column, byte character, byte foreground, byte background, bool disabledLook);
void drawStringOnDisplay(int16 x, int16 y, const char *text, byte foreground, byte background);
+ void drawStringOnDisplay(int16 x, int16 adjX, int16 y, int16 adjY, const char *text, byte foregroundColor, byte backgroundColor);
void drawCharacterOnDisplay(int16 x, int16 y, byte character, byte foreground, byte background, byte transformXOR = 0, byte transformOR = 0);
void shakeScreen(int16 repeatCount);
void updateScreen();
void initPriorityTable();
+ void createDefaultPriorityTable(uint8 *priorityTable);
void setPriorityTable(int16 priorityBase);
- void setPriority(int16 yPos, int16 priority);
+ bool saveLoadWasPriorityTableModified();
+ int16 saveLoadGetPriority(int16 yPos);
+ void saveLoadSetPriorityTableModifiedBool(bool wasModified);
+ void saveLoadSetPriority(int16 yPos, int16 priority);
+ void saveLoadFigureOutPriorityTableModifiedBool();
+
int16 priorityToY(int16 priority);
int16 priorityFromY(int16 yPos);
};
diff --git a/engines/agi/inv.cpp b/engines/agi/inv.cpp
index 1df5282622..834fa9badc 100644
--- a/engines/agi/inv.cpp
+++ b/engines/agi/inv.cpp
@@ -34,6 +34,8 @@ InventoryMgr::InventoryMgr(AgiEngine *agi, GfxMgr *gfx, TextMgr *text, SystemUI
_gfx = gfx;
_text = text;
_systemUI = systemUI;
+
+ _activeItemNr = -1;
}
InventoryMgr::~InventoryMgr() {
diff --git a/engines/agi/keyboard.cpp b/engines/agi/keyboard.cpp
index 5a73afe6fb..3bc45af5d4 100644
--- a/engines/agi/keyboard.cpp
+++ b/engines/agi/keyboard.cpp
@@ -130,7 +130,7 @@ void AgiEngine::processScummVMEvents() {
}
break;
case Common::EVENT_KEYDOWN:
- if (event.kbd.hasFlags(Common::KBD_CTRL) && event.kbd.keycode == Common::KEYCODE_d) {
+ if (event.kbd.hasFlags(Common::KBD_CTRL | Common::KBD_SHIFT) && event.kbd.keycode == Common::KEYCODE_d) {
_console->attach();
break;
}
@@ -256,7 +256,28 @@ void AgiEngine::processScummVMEvents() {
// Original AGI actually created direction events in here
// We don't do that, that's why we create a stationary event instead, which will
// result in a direction change to 0 in handleController().
- keyEnqueue(AGI_KEY_STATIONARY);
+ switch (event.kbd.keycode) {
+ case Common::KEYCODE_LEFT:
+ case Common::KEYCODE_RIGHT:
+ case Common::KEYCODE_UP:
+ case Common::KEYCODE_DOWN:
+ case Common::KEYCODE_HOME:
+ case Common::KEYCODE_END:
+ case Common::KEYCODE_PAGEUP:
+ case Common::KEYCODE_PAGEDOWN:
+ case Common::KEYCODE_KP4:
+ case Common::KEYCODE_KP6:
+ case Common::KEYCODE_KP8:
+ case Common::KEYCODE_KP2:
+ case Common::KEYCODE_KP9:
+ case Common::KEYCODE_KP3:
+ case Common::KEYCODE_KP7:
+ case Common::KEYCODE_KP1:
+ keyEnqueue(AGI_KEY_STATIONARY);
+ break;
+ default:
+ break;
+ }
}
break;
@@ -292,7 +313,8 @@ bool AgiEngine::handleMouseClicks(uint16 &key) {
if (!cycleInnerLoopIsActive()) {
// Only do this, when no inner loop is currently active
- Common::Rect displayLineRect(DISPLAY_WIDTH, FONT_DISPLAY_HEIGHT);
+ Common::Rect displayLineRect = _gfx->getFontRectForDisplayScreen(0, 0, FONT_COLUMN_CHARACTERS, 1);
+// Common::Rect displayLineRect(_gfx->getDisplayScreenWidth(), _gfx->getDisplayFontHeight());
if (displayLineRect.contains(_mouse.pos)) {
// Mouse is inside first line of the screen
@@ -307,7 +329,7 @@ bool AgiEngine::handleMouseClicks(uint16 &key) {
// Prompt is currently enabled
int16 promptRow = _text->promptRow_Get();
- displayLineRect.moveTo(0, promptRow * FONT_DISPLAY_HEIGHT);
+ displayLineRect.moveTo(0, promptRow * _gfx->getDisplayFontHeight());
if (displayLineRect.contains(_mouse.pos)) {
// and user clicked within the line of the prompt
@@ -330,9 +352,7 @@ bool AgiEngine::handleMouseClicks(uint16 &key) {
_text->stringPos_Get(stringRow, stringColumn);
stringMaxLen = _text->stringGetMaxLen();
- Common::Rect displayRect(stringMaxLen * FONT_DISPLAY_WIDTH, FONT_DISPLAY_HEIGHT);
- displayRect.moveTo(stringColumn * FONT_DISPLAY_WIDTH, stringRow * FONT_DISPLAY_HEIGHT);
-
+ Common::Rect displayRect = _gfx->getFontRectForDisplayScreen(stringColumn, stringRow, stringMaxLen, 1);
if (displayRect.contains(_mouse.pos)) {
// user clicked inside the input space
showPredictiveDialog();
@@ -472,7 +492,7 @@ bool AgiEngine::handleController(uint16 key) {
// in case you walked to the log by using the mouse, so don't!!!
int16 egoDestinationX = _mouse.pos.x;
int16 egoDestinationY = _mouse.pos.y;
- adjustPosToGameScreen(egoDestinationX, egoDestinationY);
+ _gfx->translateDisplayPosToGameScreen(egoDestinationX, egoDestinationY);
screenObjEgo->motionType = kMotionEgo;
if (egoDestinationX < (screenObjEgo->xSize / 2)) {
diff --git a/engines/agi/menu.cpp b/engines/agi/menu.cpp
index 803efd757e..49c2d0eeab 100644
--- a/engines/agi/menu.cpp
+++ b/engines/agi/menu.cpp
@@ -49,8 +49,8 @@ GfxMenu::GfxMenu(AgiEngine *vm, GfxMgr *gfx, PictureMgr *picture, TextMgr *text)
_drawnMenuNr = -1;
_drawnMenuHeight = 0;
_drawnMenuWidth = 0;
- _drawnMenuRow = 0;
- _drawnMenuColumn = 0;
+ _drawnMenuY = 0;
+ _drawnMenuX = 0;
}
GfxMenu::~GfxMenu() {
@@ -64,6 +64,8 @@ GfxMenu::~GfxMenu() {
}
void GfxMenu::addMenu(const char *menuText) {
+ int16 curColumnEnd = _setupMenuColumn;
+
// already submitted? in that case no further changes possible
if (_submitted)
return;
@@ -72,6 +74,18 @@ void GfxMenu::addMenu(const char *menuText) {
menuEntry->text = menuText;
menuEntry->textLen = menuEntry->text.size();
+
+ // Cut menu name in case menu bar is full
+ // Happens in at least the fan game Get Outta Space Quest
+ // Original interpreter had graphical issues in this case
+ // TODO: this whole code needs to get reworked anyway to support different types of menu bars depending on platform
+ curColumnEnd += menuEntry->textLen;
+ while ((menuEntry->textLen) && (curColumnEnd > 40)) {
+ menuEntry->text.deleteLastChar();
+ menuEntry->textLen--;
+ curColumnEnd--;
+ }
+
menuEntry->row = 0;
menuEntry->column = _setupMenuColumn;
menuEntry->itemCount = 0;
@@ -309,8 +323,9 @@ void GfxMenu::execute() {
// Unless we are in "via mouse" mode. In that case check current mouse position
if (viaMouse) {
- int16 mouseRow = _vm->_mouse.pos.y / FONT_DISPLAY_HEIGHT;
- int16 mouseColumn = _vm->_mouse.pos.x / FONT_DISPLAY_WIDTH;
+ int16 mouseRow = _vm->_mouse.pos.y;
+ int16 mouseColumn = _vm->_mouse.pos.x;
+ _gfx->translateDisplayPosToFontScreen(mouseColumn, mouseRow);
mouseFindMenuSelection(mouseRow, mouseColumn, _drawnMenuNr, _mouseModeItemNr);
}
@@ -354,7 +369,7 @@ void GfxMenu::execute() {
// WORKAROUND: Playarea starts right at the stop, so instead of clearing that part, render it from playarea
// Required for at least Donald Duck
// This was not done by original AGI, which means the upper pixel line were cleared in this case.
- _gfx->render_Block(0, (1 * FONT_VISUAL_HEIGHT) - 1, SCRIPT_WIDTH, FONT_VISUAL_HEIGHT);
+ _gfx->render_Block(0, 0, SCRIPT_WIDTH, FONT_VISUAL_HEIGHT);
} else {
_text->clearLine(0, 0);
}
@@ -365,6 +380,10 @@ void GfxMenu::drawMenuName(int16 menuNr, bool inverted) {
GuiMenuEntry *menuEntry = _array[menuNr];
bool disabledLook = false;
+ // Don't draw in case there is no text
+ if (!menuEntry->text.size())
+ return;
+
if (!inverted) {
_text->charAttrib_Set(0, _text->calculateTextBackground(15));
} else {
@@ -409,10 +428,11 @@ void GfxMenu::drawMenu(int16 selectedMenuNr, int16 selectedMenuItemNr) {
// calculate active menu dimensions
_drawnMenuHeight = (menuEntry->itemCount + 2) * FONT_VISUAL_HEIGHT;
_drawnMenuWidth = (menuEntry->maxItemTextLen * FONT_VISUAL_WIDTH) + 8;
- _drawnMenuRow = (menuEntry->itemCount + 3 - _text->getWindowRowMin()) * FONT_VISUAL_HEIGHT - 1;
- _drawnMenuColumn = (itemEntry->column - 1) * FONT_VISUAL_WIDTH;
+ _drawnMenuY = (1 - _text->getWindowRowMin()) * FONT_VISUAL_HEIGHT;
+ //(menuEntry->itemCount + 3 - _text->getWindowRowMin()) * FONT_VISUAL_HEIGHT - 1;
+ _drawnMenuX = (itemEntry->column - 1) * FONT_VISUAL_WIDTH;
- _gfx->drawBox(_drawnMenuColumn, _drawnMenuRow, _drawnMenuWidth, _drawnMenuHeight, 15, 0);
+ _gfx->drawBox(_drawnMenuX, _drawnMenuY, _drawnMenuWidth, _drawnMenuHeight, 15, 0);
while (itemCount) {
if (itemNr == selectedMenuItemNr) {
@@ -430,7 +450,7 @@ void GfxMenu::removeActiveMenu(int16 selectedMenuNr) {
drawMenuName(selectedMenuNr, false);
// overwrite actual menu items by rendering play screen
- _gfx->render_Block(_drawnMenuColumn, _drawnMenuRow, _drawnMenuWidth, _drawnMenuHeight);
+ _gfx->render_Block(_drawnMenuX, _drawnMenuY, _drawnMenuWidth, _drawnMenuHeight);
}
void GfxMenu::keyPress(uint16 newKey) {
@@ -531,8 +551,10 @@ void GfxMenu::keyPress(uint16 newKey) {
// In "via mouse" mode, we check if user let go of the left mouse button and then select the item that way
void GfxMenu::mouseEvent(uint16 newKey) {
// Find out, where current mouse cursor actually is
- int16 mouseRow = _vm->_mouse.pos.y / FONT_DISPLAY_HEIGHT;
- int16 mouseColumn = _vm->_mouse.pos.x / FONT_DISPLAY_WIDTH;
+ int16 mouseRow = _vm->_mouse.pos.y;
+ int16 mouseColumn = _vm->_mouse.pos.x;
+
+ _gfx->translateDisplayPosToFontScreen(mouseColumn, mouseRow);
int16 activeMenuNr, activeItemNr;
mouseFindMenuSelection(mouseRow, mouseColumn, activeMenuNr, activeItemNr);
@@ -620,7 +642,7 @@ void GfxMenu::mouseFindMenuSelection(int16 mouseRow, int16 mouseColumn, int16 &a
if (mouseRow == menuEntry->row) {
// line match
- if ((mouseColumn >= menuEntry->column) && (mouseColumn <= (menuEntry->column + menuEntry->textLen))) {
+ if ((mouseColumn >= menuEntry->column) && (mouseColumn < (menuEntry->column + menuEntry->textLen))) {
// full match
activeMenuNr = menuNr;
activeMenuItemNr = -1; // no item selected
@@ -642,7 +664,7 @@ void GfxMenu::mouseFindMenuSelection(int16 mouseRow, int16 mouseColumn, int16 &a
if (mouseRow == itemEntry->row) {
// line match
- if ((mouseColumn >= itemEntry->column) && (mouseColumn <= (itemEntry->column + itemEntry->textLen))) {
+ if ((mouseColumn >= itemEntry->column) && (mouseColumn < (itemEntry->column + itemEntry->textLen))) {
// full match
if (itemEntry->enabled) {
// Only see it, when it's currently enabled
diff --git a/engines/agi/menu.h b/engines/agi/menu.h
index a621d7f0f2..b47289180b 100644
--- a/engines/agi/menu.h
+++ b/engines/agi/menu.h
@@ -111,8 +111,8 @@ private:
uint16 _drawnMenuHeight;
uint16 _drawnMenuWidth;
- int16 _drawnMenuRow;
- int16 _drawnMenuColumn;
+ int16 _drawnMenuY;
+ int16 _drawnMenuX;
// Following variables are used in "via mouse" mode
int16 _mouseModeItemNr;
diff --git a/engines/agi/motion.cpp b/engines/agi/motion.cpp
index 7f49028701..f408ba35e6 100644
--- a/engines/agi/motion.cpp
+++ b/engines/agi/motion.cpp
@@ -62,6 +62,60 @@ void AgiEngine::changePos(ScreenObjEntry *screenObj) {
}
}
+// WORKAROUND:
+// A motion was just activated, check if "end.of.loop"/"reverse.loop" is currently active for the same screen object
+// If this is the case, it would result in some random flag getting overwritten in original AGI after the loop was
+// completed, because in original AGI loop_flag + wander_count/follow_stepSize/move_X shared the same memory location.
+// This is basically an implementation error in the original interpreter.
+// Happens in at least:
+// - BC: right at the end when the witches disappear at least on Apple IIgs (room 12, screen object 13, view 84)
+// - KQ1: when grabbing the eagle (room 22).
+// - KQ2: happened somewhere in the game, LordHoto couldn't remember exactly where
+void AgiEngine::motionActivated(ScreenObjEntry *screenObj) {
+ if (screenObj->flags & fCycling) {
+ // Cycling active too
+ switch (screenObj->cycle) {
+ case kCycleEndOfLoop: // "end.of.loop"
+ case kCycleRevLoop: // "reverse.loop"
+ // Disable it
+ screenObj->flags &= ~fCycling;
+ screenObj->cycle = kCycleNormal;
+
+ warning("Motion activated for screen object %d, but cycler also active", screenObj->objectNr);
+ warning("This would have resulted in flag corruption in original AGI. Cycler disabled.");
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+// WORKAROUND:
+// See comment for motionActivated()
+// This way no flag would have been overwritten, but certain other variables of the motions.
+void AgiEngine::cyclerActivated(ScreenObjEntry *screenObj) {
+ switch (screenObj->motionType) {
+ case kMotionWander:
+ // this would have resulted in wander_count to get corrupted
+ // We don't stop it.
+ break;
+ case kMotionFollowEgo:
+ // this would have resulted in follow_stepSize to get corrupted
+ // do not stop motion atm - screenObj->direction = 0;
+ // do not stop motion atm - screenObj->motionType = kMotionNormal;
+ break;
+ case kMotionMoveObj:
+ // this would have resulted in move_x to get corrupted
+ // do not stop motion atm - motionMoveObjStop(screenObj);
+ break;
+ default:
+ return;
+ break;
+ }
+ warning("Cycler activated for screen object %d, but motion also active", screenObj->objectNr);
+ warning("This would have resulted in corruption in original AGI. Motion disabled.");
+}
+
void AgiEngine::motionWander(ScreenObjEntry *screenObj) {
uint8 originalWanderCount = screenObj->wander_count;
diff --git a/engines/agi/op_cmd.cpp b/engines/agi/op_cmd.cpp
index 3f8630521d..fed07ea986 100644
--- a/engines/agi/op_cmd.cpp
+++ b/engines/agi/op_cmd.cpp
@@ -37,7 +37,6 @@
namespace Agi {
-#define getGameID() state->_vm->getGameID()
#define getFeatures() state->_vm->getFeatures()
#define getVersion() state->_vm->getVersion()
#define getLanguage() state->_vm->getLanguage()
@@ -82,7 +81,7 @@ void cmdAssignN(AgiGame *state, AgiEngine *vm, uint8 *parameter) {
// variable to the correct value here
// Fixes bug #1942476 - "AGI: Fan(Get Outta SQ) - Score
// is lost on restart"
- if (getGameID() == GID_GETOUTTASQ && varNr == 7)
+ if (vm->getGameID() == GID_GETOUTTASQ && varNr == 7)
vm->setVar(varNr, 8);
}
@@ -890,6 +889,19 @@ void cmdObjStatusF(AgiGame *state, AgiEngine *vm, uint8 *parameter) {
// unk_181: Deactivate keypressed control (default control of ego)
void cmdSetSimple(AgiGame *state, AgiEngine *vm, uint8 *parameter) {
if (!(getFeatures() & (GF_AGI256 | GF_AGI256_2))) {
+ // set.simple is called by Larry 1 on Apple IIgs at the store, after answering the 555-6969 phone.
+ // load.sound(16) is called right before it. Interpreter is 2.440-like.
+ // it's called with parameter 16.
+ // Original interpreter doesn't seem to play any sound.
+ // TODO: Figure out what's going on. It can't be automatic saving of course.
+ // Also getting called in KQ1, when planting beans - parameter 12.
+ // And when killing the witch - parameter 40.
+ if ((getVersion() < 0x2425) || (getVersion() == 0x2440)) {
+ // was not available before 2.2425, but also not available in 2.440
+ warning("set.simple called, although not available for current AGI version");
+ return;
+ }
+
int16 stringNr = parameter[0];
const char *textPtr = nullptr;
@@ -897,7 +909,10 @@ void cmdSetSimple(AgiGame *state, AgiEngine *vm, uint8 *parameter) {
// Try to get description for automatic saves
textPtr = state->strings[stringNr];
+
strncpy(state->automaticSaveDescription, textPtr, sizeof(state->automaticSaveDescription));
+ state->automaticSaveDescription[sizeof(state->automaticSaveDescription) - 1] = 0;
+
if (state->automaticSaveDescription[0]) {
// We got it and it's set, so enable automatic saving
state->automaticSave = true;
@@ -917,21 +932,26 @@ void cmdSetSimple(AgiGame *state, AgiEngine *vm, uint8 *parameter) {
spritesMgr->drawAllSpriteLists();
state->pictureShown = false;
+ // Loading trigger
+ vm->artificialDelayTrigger_DrawPicture(resourceNr);
+
// Show the picture. Similar to void cmdShow_pic(AgiGame *state, AgiEngine *vm, uint8 *p).
vm->setFlag(VM_FLAG_OUTPUT_MODE, false);
vm->_text->closeWindow();
vm->_picture->showPic();
state->pictureShown = true;
-
- // Loading trigger
- vm->loadingTrigger_DrawPicture();
}
}
+// push.script was not available until 2.425, and also not available in 2.440
void cmdPopScript(AgiGame *state, AgiEngine *vm, uint8 *parameter) {
- if (getVersion() >= 0x2915) {
- debug(0, "pop.script");
+ if ((getVersion() < 0x2425) || (getVersion() == 0x2440)) {
+ // was not available before 2.2425, but also not available in 2.440
+ warning("pop.script called, although not available for current AGI version");
+ return;
}
+
+ debug(0, "pop.script");
}
void cmdDiscardSound(AgiGame *state, AgiEngine *vm, uint8 *parameter) {
@@ -953,8 +973,18 @@ void cmdShowMouse(AgiGame *state, AgiEngine *vm, uint8 *parameter) {
// but show.mouse is never called afterwards. Game running under emulator doesn't seem to hide the mouse cursor.
// TODO: figure out, what exactly happens. Probably some hacked-in command and not related to mouse cursor for that game?
void cmdHideMouse(AgiGame *state, AgiEngine *vm, uint8 *parameter) {
- if (getVersion() < 0x3000)
+ if (getVersion() < 0x3000) {
+ // was not available before 3.086
+ warning("hide.mouse, although not available for current AGI version");
+ return;
+ }
+
+ if ((vm->getGameID() == GID_MH1) && (vm->getPlatform() == Common::kPlatformApple2GS)) {
+ // Called right after beating arcade sequence on day 4 in the hospital Parameter is "1".
+ // Right before cutscene. show.mouse isn't called. Probably different function.
+ warning("hide.mouse called, disabled for MH1 Apple IIgs");
return;
+ }
// WORKAROUND: Turns off current movement that's being caused with the mouse.
// This fixes problems with too many popup boxes appearing in the Amiga
@@ -972,14 +1002,18 @@ void cmdHideMouse(AgiGame *state, AgiEngine *vm, uint8 *parameter) {
}
void cmdAllowMenu(AgiGame *state, AgiEngine *vm, uint8 *parameter) {
+ if (getVersion() < 0x3098) {
+ // was not available before 3.098
+ warning("allow.menu called, although not available for current AGI version");
+ return;
+ }
+
uint16 allowed = parameter[0];
- if (getVersion() >= 0x3098) {
- if (allowed) {
- state->_vm->_menu->accessAllow();
- } else {
- state->_vm->_menu->accessDeny();
- }
+ if (allowed) {
+ state->_vm->_menu->accessAllow();
+ } else {
+ state->_vm->_menu->accessDeny();
}
}
@@ -997,15 +1031,21 @@ void cmdFenceMouse(AgiGame *state, AgiEngine *vm, uint8 *parameter) {
// HoldKey was added in 2.425
// There was no way to disable this mode until 3.098 though
void cmdHoldKey(AgiGame *state, AgiEngine *vm, uint8 *parameter) {
- if (getVersion() < 0x2425)
+ if ((getVersion() < 0x2425) || (getVersion() == 0x2440)) {
+ // was not available before 2.425, but also not available in 2.440
+ warning("hold.key called, although not available for current AGI version");
return;
+ }
vm->_keyHoldMode = true;
}
void cmdReleaseKey(AgiGame *state, AgiEngine *vm, uint8 *parameter) {
- if (getVersion() < 0x3098)
+ if (getVersion() < 0x3098) {
+ // was not available before 3.098
+ warning("release.key called, although not available for current AGI version");
return;
+ }
vm->_keyHoldMode = false;
}
@@ -1094,7 +1134,7 @@ void cmdDrawPicV1(AgiGame *state, AgiEngine *vm, uint8 *parameter) {
vm->_text->promptClear();
// Loading trigger
- vm->loadingTrigger_DrawPicture();
+ vm->artificialDelayTrigger_DrawPicture(resourceNr);
}
void cmdDrawPic(AgiGame *state, AgiEngine *vm, uint8 *parameter) {
@@ -1106,6 +1146,7 @@ void cmdDrawPic(AgiGame *state, AgiEngine *vm, uint8 *parameter) {
spritesMgr->eraseSprites();
vm->_picture->decodePicture(resourceNr, true);
+
spritesMgr->buildAllSpriteLists();
spritesMgr->drawAllSpriteLists();
state->pictureShown = false;
@@ -1123,11 +1164,11 @@ void cmdDrawPic(AgiGame *state, AgiEngine *vm, uint8 *parameter) {
// above the ground), flag 103 is reset, thereby fixing this issue. Note
// that this is a script bug and occurs in the original interpreter as well.
// Fixes bug #3056: AGI: SQ1 (2.2 DOS ENG) bizzare exploding roger
- if (getGameID() == GID_SQ1 && resourceNr == 20)
+ if (vm->getGameID() == GID_SQ1 && resourceNr == 20)
vm->setFlag(103, false);
// Loading trigger
- vm->loadingTrigger_DrawPicture();
+ vm->artificialDelayTrigger_DrawPicture(resourceNr);
}
void cmdShowPic(AgiGame *state, AgiEngine *vm, uint8 *parameter) {
@@ -1179,7 +1220,7 @@ void cmdOverlayPic(AgiGame *state, AgiEngine *vm, uint8 *parameter) {
state->pictureShown = false;
// Loading trigger
- vm->loadingTrigger_DrawPicture();
+ vm->artificialDelayTrigger_DrawPicture(resourceNr);
}
void cmdShowPriScreen(AgiGame *state, AgiEngine *vm, uint8 *parameter) {
@@ -1454,6 +1495,8 @@ void cmdReverseLoop(AgiGame *state, AgiEngine *vm, uint8 *parameter) {
screenObj->flags |= (fDontupdate | fUpdate | fCycling);
screenObj->loop_flag = loopFlag;
state->_vm->setFlag(screenObj->loop_flag, false);
+
+ vm->cyclerActivated(screenObj);
}
void cmdReverseLoopV1(AgiGame *state, AgiEngine *vm, uint8 *parameter) {
@@ -1479,6 +1522,8 @@ void cmdEndOfLoop(AgiGame *state, AgiEngine *vm, uint8 *parameter) {
screenObj->flags |= (fDontupdate | fUpdate | fCycling);
screenObj->loop_flag = loopFlag;
vm->setFlag(screenObj->loop_flag, false);
+
+ vm->cyclerActivated(screenObj);
}
void cmdEndOfLoopV1(AgiGame *state, AgiEngine *vm, uint8 *parameter) {
@@ -1591,6 +1636,8 @@ void cmdFollowEgo(AgiGame *state, AgiEngine *vm, uint8 *parameter) {
vm->setFlag(screenObj->follow_flag, false);
screenObj->flags |= fUpdate;
}
+
+ vm->motionActivated(screenObj);
}
void cmdMoveObj(AgiGame *state, AgiEngine *vm, uint8 *parameter) {
@@ -1619,6 +1666,8 @@ void cmdMoveObj(AgiGame *state, AgiEngine *vm, uint8 *parameter) {
screenObj->flags |= fUpdate;
}
+ vm->motionActivated(screenObj);
+
if (objectNr == 0)
state->playerControl = false;
@@ -1647,6 +1696,8 @@ void cmdMoveObjF(AgiGame *state, AgiEngine *vm, uint8 *parameter) {
vm->setFlag(screenObj->move_flag, false);
screenObj->flags |= fUpdate;
+ vm->motionActivated(screenObj);
+
if (objectNr == 0)
state->playerControl = false;
@@ -1668,6 +1719,8 @@ void cmdWander(AgiGame *state, AgiEngine *vm, uint8 *parameter) {
} else {
screenObj->flags |= fUpdate;
}
+
+ vm->motionActivated(screenObj);
}
void cmdSetGameID(AgiGame *state, AgiEngine *vm, uint8 *parameter) {
@@ -1857,7 +1910,7 @@ void cmdDistance(AgiGame *state, AgiEngine *vm, uint8 *parameter) {
// wouldn't chase Rosella around anymore. If it had worked correctly the zombie
// wouldn't have come up at all or it would have come up and gone back down
// immediately. The latter approach is the one implemented here.
- if (getGameID() == GID_KQ4 && (vm->getVar(VM_VAR_CURRENT_ROOM) == 16 || vm->getVar(VM_VAR_CURRENT_ROOM) == 18) && destVarNr >= 221 && destVarNr <= 223) {
+ if (vm->getGameID() == GID_KQ4 && (vm->getVar(VM_VAR_CURRENT_ROOM) == 16 || vm->getVar(VM_VAR_CURRENT_ROOM) == 18) && destVarNr >= 221 && destVarNr <= 223) {
// Rooms 16 and 18 are graveyards where three zombies come up at night. They use logics 16 and 18.
// Variables 221-223 are used to save the distance between each zombie and Rosella.
// Variables 155, 156 and 162 are used to save the state of each zombie in room 16.
@@ -2132,6 +2185,7 @@ void cmdPrintAtV(AgiGame *state, AgiEngine *vm, uint8 *parameter) {
state->_vm->_text->printAt(textNr, textRow, textColumn, textWidth);
}
+// push.script was not available until 2.425, and also not available in 2.440
void cmdPushScript(AgiGame *state, AgiEngine *vm, uint8 *parameter) {
// We run AGIMOUSE always as a side effect
//if (getFeatures() & GF_AGIMOUSE || true) {
@@ -2146,6 +2200,20 @@ void cmdPushScript(AgiGame *state, AgiEngine *vm, uint8 *parameter) {
}
void cmdSetPriBase(AgiGame *state, AgiEngine *vm, uint8 *parameter) {
+ if ((getVersion() != 0x2425) && (getVersion() < 0x2936)) {
+ // was only available in the 2.425 interpreter and from 2.936 (last AGI2 version) onwards
+ // Called during KQ3 (Apple IIgs):
+ // - picking up chicken (parameter = 50)
+ // - opening store/tavern door (parameter = 19)
+ // - when pirates say "Land Ho" (parameter = 16)
+ // - when killing the dragon (parameter = 4)
+ // Also called by SQ2 (Apple IIgs):
+ // - in Vohaul's lair (SQ2 currently gets this call through, which breaks some priority)
+ // TODO: Figure out what's going on
+ warning("set.pri.base called, although not available for current AGI version");
+ return;
+ }
+
uint16 priorityBase = parameter[0];
debug(0, "Priority base set to %d", priorityBase);
@@ -2159,7 +2227,7 @@ void cmdMousePosn(AgiGame *state, AgiEngine *vm, uint8 *parameter) {
int16 mouseX = vm->_mouse.pos.x;
int16 mouseY = vm->_mouse.pos.y;
- state->_vm->adjustPosToGameScreen(mouseX, mouseY);
+ vm->_gfx->translateDisplayPosToGameScreen(mouseX, mouseY);
vm->setVar(destVarNr1, mouseX);
vm->setVar(destVarNr2, mouseY);
@@ -2285,6 +2353,9 @@ int AgiEngine::runLogic(int16 logicNr) {
}
#endif
+ // Just a counter for every instruction, that got executed
+ _instructionCounter++;
+
_game.execStack.back().curIP = state->_curLogic->cIP;
char st[101];
diff --git a/engines/agi/opcodes.cpp b/engines/agi/opcodes.cpp
index 359d79ee4a..472917de77 100644
--- a/engines/agi/opcodes.cpp
+++ b/engines/agi/opcodes.cpp
@@ -343,18 +343,18 @@ AgiInstruction insV2[] = {
{ "div.n", "vn", &cmdDivN }, // B7
{ "div.v", "vv", &cmdDivV }, // B8
{ "close.window", "", &cmdCloseWindow }, // B9
- { "set.simple", "n", &cmdSetSimple }, // BA
+ { "set.simple", "n", &cmdSetSimple }, // BA AGI2.425+, *BUT* not included in AGI2.440
{ "push.script", "", &cmdPushScript }, // BB
{ "pop.script", "", &cmdPopScript }, // BC
{ "hold.key", "", &cmdHoldKey }, // BD
- { "set.pri.base", "n", &cmdSetPriBase }, // BE
- { "discard.sound", "n", &cmdDiscardSound }, // BF
- { "hide.mouse", "", &cmdHideMouse }, // 1 arg for AGI version 3.002.086
+ { "set.pri.base", "n", &cmdSetPriBase }, // BE AGI2.936+ *AND* also inside AGI2.425
+ { "discard.sound", "n", &cmdDiscardSound }, // BF was skip for PC
+ { "hide.mouse", "", &cmdHideMouse }, // C0 1 arg for AGI version 3.002.086 AGI3+ only starts here
{ "allow.menu", "n", &cmdAllowMenu }, // C1
{ "show.mouse", "", &cmdShowMouse }, // C2
{ "fence.mouse", "nnnn", &cmdFenceMouse }, // C3
{ "mouse.posn", "vv", &cmdMousePosn }, // C4
- { "release.key", "", &cmdReleaseKey }, // 2 args for at least the Amiga GR (v2.05 1989-03-09) using AGI 2.316
+ { "release.key", "", &cmdReleaseKey }, // C5 2 args for at least the Amiga GR (v2.05 1989-03-09) using AGI 2.316
{ "adj.ego.move.to.xy", "", &cmdAdjEgoMoveToXY } // C6
};
diff --git a/engines/agi/palette.h b/engines/agi/palette.h
index f66582b9b6..40c31da425 100644
--- a/engines/agi/palette.h
+++ b/engines/agi/palette.h
@@ -60,6 +60,22 @@ static const uint8 PALETTE_CGA[4 * 3] = {
};
/**
+ * 2 color Hercules (green) palette. Using 8-bit RGB values.
+ */
+static const uint8 PALETTE_HERCULES_GREEN[2 * 3] = {
+ 0x00, 0x00, 0x00, // black
+ 0x00, 0xdc, 0x28 // green
+};
+
+/**
+ * 2 color Hercules (amber) palette. Using 8-bit RGB values.
+ */
+static const uint8 PALETTE_HERCULES_AMBER[2 * 3] = {
+ 0x00, 0x00, 0x00, // black
+ 0xdc, 0xb4, 0x00 // amber
+};
+
+/**
* Atari ST AGI palette.
* Used by all of the tested Atari ST AGI games
* from Donald Duck's Playground (1986) to Manhunter II (1989).
@@ -122,6 +138,9 @@ static const uint8 PALETTE_APPLE_II_GS[16 * 3] = {
0xF, 0xF, 0xF
};
+// Re-use Amiga v1 palette for Apple IIgs Space Quest 1
+#define PALETTE_APPLE_II_GS_SQ1 PALETTE_AMIGA_V1
+
/**
* First generation Amiga & Apple IIGS AGI palette.
* A 16-color, 12-bit RGB palette.
diff --git a/engines/agi/picture.cpp b/engines/agi/picture.cpp
index 36eb587f68..a80e811f44 100644
--- a/engines/agi/picture.cpp
+++ b/engines/agi/picture.cpp
@@ -394,9 +394,6 @@ void PictureMgr::drawPictureC64() {
_patCode = getNextByte();
plotBrush();
break;
- case 0xfb:
- draw_LineShort();
- break;
case 0xff: // end of data
return;
default:
@@ -433,6 +430,9 @@ void PictureMgr::drawPictureV1() {
_scrOn = true;
_priOn = false;
break;
+ case 0xfb:
+ draw_LineShort();
+ break;
case 0xff: // end of data
return;
default:
@@ -1001,7 +1001,7 @@ void PictureMgr::clear() {
void PictureMgr::showPic() {
debugC(8, kDebugLevelMain, "Show picture!");
- _gfx->render_Block(0, 167, SCRIPT_WIDTH, SCRIPT_HEIGHT);
+ _gfx->render_Block(0, 0, SCRIPT_WIDTH, SCRIPT_HEIGHT);
}
/**
@@ -1014,8 +1014,7 @@ void PictureMgr::showPic(int16 x, int16 y, int16 pic_width, int16 pic_height) {
debugC(8, kDebugLevelMain, "Show picture!");
- // render block requires lower left coordinate!
- _gfx->render_Block(x, pic_height + y - 1, pic_width, pic_height);
+ _gfx->render_Block(x, y, pic_width, pic_height);
}
void PictureMgr::showPicWithTransition() {
@@ -1038,13 +1037,13 @@ void PictureMgr::showPicWithTransition() {
case Common::kRenderAmiga:
case Common::kRenderApple2GS:
// Platform Amiga/Apple II GS -> render and do Amiga transition
- _gfx->render_Block(0, 167, SCRIPT_WIDTH, SCRIPT_HEIGHT, false);
+ _gfx->render_Block(0, 0, SCRIPT_WIDTH, SCRIPT_HEIGHT, false);
_gfx->transition_Amiga();
return;
break;
case Common::kRenderAtariST:
// Platform Atari ST used a different transition, looks "high-res" (full 320x168)
- _gfx->render_Block(0, 167, SCRIPT_WIDTH, SCRIPT_HEIGHT, false);
+ _gfx->render_Block(0, 0, SCRIPT_WIDTH, SCRIPT_HEIGHT, false);
_gfx->transition_AtariSt();
return;
default:
@@ -1054,7 +1053,7 @@ void PictureMgr::showPicWithTransition() {
}
}
- _gfx->render_Block(0, 167, SCRIPT_WIDTH, SCRIPT_HEIGHT);
+ _gfx->render_Block(0, 0, SCRIPT_WIDTH, SCRIPT_HEIGHT);
}
// preagi needed functions (for plotPattern)
diff --git a/engines/agi/preagi.cpp b/engines/agi/preagi.cpp
index 104b442f5d..bb5d3b8896 100644
--- a/engines/agi/preagi.cpp
+++ b/engines/agi/preagi.cpp
@@ -58,7 +58,7 @@ void PreAgiEngine::initialize() {
initRenderMode();
_font = new GfxFont(this);
- _gfx = new GfxMgr(this);
+ _gfx = new GfxMgr(this, _font);
_picture = new PictureMgr(this, _gfx);
_font->init();
@@ -112,7 +112,7 @@ void PreAgiEngine::clearScreen(int attr, bool overrideDefault) {
}
void PreAgiEngine::clearGfxScreen(int attr) {
- _gfx->drawDisplayRect(0, 0, GFX_WIDTH - 1, IDI_MAX_ROW_PIC * 8 - 1, (attr & 0xF0) / 0x10);
+ _gfx->drawDisplayRect(0, 0, DISPLAY_DEFAULT_WIDTH - 1, IDI_MAX_ROW_PIC * 8 - 1, (attr & 0xF0) / 0x10);
}
// String functions
diff --git a/engines/agi/preagi_mickey.cpp b/engines/agi/preagi_mickey.cpp
index 8b1ae81b8e..620d5e0baf 100644
--- a/engines/agi/preagi_mickey.cpp
+++ b/engines/agi/preagi_mickey.cpp
@@ -957,7 +957,7 @@ void MickeyEngine::drawLogo() {
}
}
- _gfx->copyDisplayRectToScreen(0, 0, DISPLAY_WIDTH, height);
+ _gfx->copyDisplayToScreen();
delete[] fileBuffer;
}
diff --git a/engines/agi/saveload.cpp b/engines/agi/saveload.cpp
index 1bf0dbcc02..0658609cd0 100644
--- a/engines/agi/saveload.cpp
+++ b/engines/agi/saveload.cpp
@@ -45,23 +45,25 @@
#include "agi/systemui.h"
#include "agi/words.h"
-#define SAVEGAME_CURRENT_VERSION 8
+#define SAVEGAME_CURRENT_VERSION 11
//
-// Version 0 (Sarien): view table has 64 entries
-// Version 1 (Sarien): view table has 256 entries (needed in KQ3)
-// Version 2 (ScummVM): first ScummVM version
-// Version 3 (ScummVM): added AGIPAL save/load support
-// Version 4 (ScummVM): added thumbnails and save creation date/time
-// Version 5 (ScummVM): Added game md5
-// Version 6 (ScummVM): Added game played time
-// Version 7 (ScummVM): Added controller key mappings
-// required for some games for quick-loading from ScummVM main menu
-// for games, that do not set all key mappings right at the start
-// Added automatic save data (for command SetSimple)
-// Version 8 (ScummVM): Added Hold-Key-Mode boolean
-// required for at least Mixed Up Mother Goose
-// gets set at the start of the game only
+// Version 0 (Sarien): view table has 64 entries
+// Version 1 (Sarien): view table has 256 entries (needed in KQ3)
+// Version 2 (ScummVM): first ScummVM version
+// Version 3 (ScummVM): added AGIPAL save/load support
+// Version 4 (ScummVM): added thumbnails and save creation date/time
+// Version 5 (ScummVM): Added game md5
+// Version 6 (ScummVM): Added game played time
+// Version 7 (ScummVM): Added controller key mappings
+// required for some games for quick-loading from ScummVM main menu
+// for games, that do not set all key mappings right at the start
+// Added automatic save data (for command SetSimple)
+// Version 8 (ScummVM): Added Hold-Key-Mode boolean
+// required for at least Mixed Up Mother Goose
+// gets set at the start of the game only
+// Version 9 (ScummVM): Added seconds to saved game time stamp
+// Version 10 (ScummVM): Added priorityTableSet boolean
namespace Agi {
@@ -109,6 +111,8 @@ int AgiEngine::saveGame(const Common::String &fileName, const Common::String &de
debugC(5, kDebugLevelMain | kDebugLevelSavegame, "Writing save date (%d)", saveDate);
out->writeUint16BE(saveTime);
debugC(5, kDebugLevelMain | kDebugLevelSavegame, "Writing save time (%d)", saveTime);
+ // Version 9+: save seconds of current time as well
+ out->writeByte(curTime.tm_sec & 0xFF);
out->writeUint32BE(playTime);
debugC(5, kDebugLevelMain | kDebugLevelSavegame, "Writing play time (%d)", playTime);
@@ -171,9 +175,11 @@ int AgiEngine::saveGame(const Common::String &fileName, const Common::String &de
out->writeSint16BE(0);
}
- // TODO: save if priority table was modified
for (i = 0; i < SCRIPT_HEIGHT; i++)
- out->writeByte(_gfx->priorityFromY(i));
+ out->writeByte(_gfx->saveLoadGetPriority(i));
+
+ // Version 10+: Save, if priority table got modified (set.pri.base opcode)
+ out->writeSint16BE((int16)_gfx->saveLoadWasPriorityTableModified());
out->writeSint16BE((int16)_game.gfxMode);
out->writeByte(_text->inputGetCursorChar());
@@ -260,6 +266,8 @@ int AgiEngine::saveGame(const Common::String &fileName, const Common::String &de
out->writeByte(screenObj->motionType);
out->writeByte(screenObj->cycle);
+ // Version 11+: loop_flag, was saved previously under vt.parm1
+ out->writeByte(screenObj->loop_flag);
out->writeByte(screenObj->priority);
out->writeUint16BE(screenObj->flags);
@@ -338,6 +346,7 @@ int AgiEngine::loadGame(const Common::String &fileName, bool checkId) {
int16 parm[7];
Common::InSaveFile *in;
bool totalPlayTimeWasSet = false;
+ byte oldLoopFlag = 0;
debugC(3, kDebugLevelMain | kDebugLevelSavegame, "AgiEngine::loadGame(%s)", fileName.c_str());
@@ -384,7 +393,10 @@ int AgiEngine::loadGame(const Common::String &fileName, bool checkId) {
Graphics::skipThumbnail(*in);
in->readUint32BE(); // save date
- in->readUint16BE(); // save time
+ in->readUint16BE(); // save time (hour + minute)
+ if (saveVersion >= 9) {
+ in->readByte(); // save time seconds
+ }
if (saveVersion >= 6) {
uint32 playTime = in->readUint32BE();
inGameTimerReset(playTime * 1000);
@@ -494,7 +506,21 @@ int AgiEngine::loadGame(const Common::String &fileName, bool checkId) {
}
for (i = 0; i < SCRIPT_HEIGHT; i++)
- _gfx->setPriority(i, in->readByte());
+ _gfx->saveLoadSetPriority(i, in->readByte());
+
+ if (saveVersion >= 10) {
+ // Version 10+: priority table was modified by scripts
+ int16 priorityTableWasModified = in->readSint16BE();
+
+ if (priorityTableWasModified) {
+ _gfx->saveLoadSetPriorityTableModifiedBool(true);
+ } else {
+ _gfx->saveLoadSetPriorityTableModifiedBool(false);
+ }
+ } else {
+ // Try to figure it out by ourselves
+ _gfx->saveLoadFigureOutPriorityTableModifiedBool();
+ }
_text->closeWindow();
@@ -611,6 +637,10 @@ int AgiEngine::loadGame(const Common::String &fileName, bool checkId) {
screenObj->motionType = (MotionType)in->readByte();
screenObj->cycle = (CycleType)in->readByte();
+ if (saveVersion >= 11) {
+ // Version 11+: loop_flag, was previously vt.parm1
+ screenObj->loop_flag = in->readByte();
+ }
screenObj->priority = in->readByte();
screenObj->flags = in->readUint16BE();
@@ -618,7 +648,7 @@ int AgiEngine::loadGame(const Common::String &fileName, bool checkId) {
// this was done so that saved games compatibility isn't broken
switch (screenObj->motionType) {
case kMotionNormal:
- in->readByte();
+ oldLoopFlag = in->readByte();
in->readByte();
in->readByte();
in->readByte();
@@ -628,12 +658,14 @@ int AgiEngine::loadGame(const Common::String &fileName, bool checkId) {
in->readByte();
in->readByte();
in->readByte();
+ oldLoopFlag = screenObj->wander_count;
break;
case kMotionFollowEgo:
screenObj->follow_stepSize = in->readByte();
screenObj->follow_flag = in->readByte();
screenObj->follow_count = in->readByte();
in->readByte();
+ oldLoopFlag = screenObj->follow_stepSize;
break;
case kMotionEgo:
case kMotionMoveObj:
@@ -641,10 +673,21 @@ int AgiEngine::loadGame(const Common::String &fileName, bool checkId) {
screenObj->move_y = in->readByte();
screenObj->move_stepSize = in->readByte();
screenObj->move_flag = in->readByte();
+ oldLoopFlag = screenObj->move_x;
break;
default:
error("unknown motion-type");
}
+ if (saveVersion < 11) {
+ if (saveVersion < 7) {
+ // Recreate loop_flag from motion-type (was previously vt.parm1)
+ // vt.parm1 was shared for multiple uses
+ screenObj->loop_flag = oldLoopFlag;
+ } else {
+ // for Version 7-10 we can't really do anything, it was not saved
+ screenObj->loop_flag = 0; // set it to 0
+ }
+ }
}
for (i = vtEntries; i < SCREENOBJECTS_MAX; i++) {
memset(&_game.screenObjTable[i], 0, sizeof(ScreenObjEntry));
@@ -693,7 +736,7 @@ int AgiEngine::loadGame(const Common::String &fileName, bool checkId) {
_words->clearEgoWords();
// don't delay anything right after restoring a game
- nonBlockingText_Forget();
+ artificialDelay_Reset();
_sprites->eraseSprites();
_sprites->buildAllSpriteLists();
@@ -704,7 +747,7 @@ int AgiEngine::loadGame(const Common::String &fileName, bool checkId) {
_text->promptRedraw();
// copy everything over (we should probably only copy over the remaining parts of the screen w/o play screen
- _gfx->copyDisplayRectToScreen(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT);
+ _gfx->copyDisplayToScreen();
// Sync volume settings from ScummVM system settings, so that VM volume variable is overwritten
setVolumeViaSystemSetting();
@@ -813,7 +856,7 @@ Common::String AgiEngine::getSavegameFilename(int16 slotId) const {
return saveLoadSlot;
}
-bool AgiEngine::getSavegameInformation(int16 slotId, Common::String &saveDescription, uint32 &saveDate, uint16 &saveTime, bool &saveIsValid) {
+bool AgiEngine::getSavegameInformation(int16 slotId, Common::String &saveDescription, uint32 &saveDate, uint32 &saveTime, bool &saveIsValid) {
Common::InSaveFile *in;
Common::String fileName = getSavegameFilename(slotId);
char saveGameDescription[31];
@@ -875,7 +918,10 @@ bool AgiEngine::getSavegameInformation(int16 slotId, Common::String &saveDescrip
Graphics::skipThumbnail(*in);
saveDate = in->readUint32BE();
- saveTime = in->readUint16BE();
+ saveTime = in->readUint16BE() << 8;
+ if (saveVersion >= 9) {
+ saveTime |= in->readByte(); // add seconds (only available since saved game version 9+)
+ }
// save date is DDMMYYYY, we need a proper format
byte saveDateDay = saveDate >> 24;
diff --git a/engines/agi/sound_2gs.cpp b/engines/agi/sound_2gs.cpp
index b33591343a..b1bcee3920 100644
--- a/engines/agi/sound_2gs.cpp
+++ b/engines/agi/sound_2gs.cpp
@@ -482,6 +482,8 @@ static bool convertWave(Common::SeekableReadStream &source, int8 *dest, uint len
IIgsSample::IIgsSample(uint8 *data, uint32 len, int16 resourceNr) : AgiSound() {
Common::MemoryReadStream stream(data, len, DisposeAfterUse::YES);
+ _sample = nullptr;
+
// Check that the header was read ok and that it's of the correct type
if (_header.read(stream) && _header.type == AGI_SOUND_SAMPLE) { // An Apple IIGS AGI sample resource
uint32 sampleStartPos = stream.pos();
diff --git a/engines/agi/sprite.cpp b/engines/agi/sprite.cpp
index 74f20c1d22..8263ea12ac 100644
--- a/engines/agi/sprite.cpp
+++ b/engines/agi/sprite.cpp
@@ -106,6 +106,28 @@ void SpritesMgr::buildSpriteListAdd(uint16 givenOrderNr, ScreenObjEntry *screenO
spriteEntry.yPos = (screenObj->yPos) - (screenObj->ySize) + 1;
spriteEntry.xSize = screenObj->xSize;
spriteEntry.ySize = screenObj->ySize;
+
+ // Checking, if xPos/yPos/right/bottom are valid and do not go outside of playscreen (visual screen)
+ // Original AGI did not do this (but it then resulted in memory corruption)
+ if (spriteEntry.xPos < 0) {
+ warning("buildSpriteListAdd(): ignoring screen obj %d, b/c xPos (%d) < 0", screenObj->objectNr, spriteEntry.xPos);
+ return;
+ }
+ if (spriteEntry.yPos < 0) {
+ warning("buildSpriteListAdd(): ignoring screen obj %d, b/c yPos (%d) < 0", screenObj->objectNr, spriteEntry.yPos);
+ return;
+ }
+ int16 xRight = spriteEntry.xPos + spriteEntry.xSize;
+ if (xRight > SCRIPT_HEIGHT) {
+ warning("buildSpriteListAdd(): ignoring screen obj %d, b/c rightPos (%d) > %d", screenObj->objectNr, xRight, SCRIPT_WIDTH);
+ return;
+ }
+ int16 yBottom = spriteEntry.yPos + spriteEntry.ySize;
+ if (yBottom > SCRIPT_HEIGHT) {
+ warning("buildSpriteListAdd(): ignoring screen obj %d, b/c bottomPos (%d) > %d", screenObj->objectNr, yBottom, SCRIPT_HEIGHT);
+ return;
+ }
+
// warning("list-add: %d, %d, original yPos: %d, ySize: %d", spriteEntry.xPos, spriteEntry.yPos, screenObj->yPos, screenObj->ySize);
spriteEntry.backgroundBuffer = (uint8 *)malloc(spriteEntry.xSize * spriteEntry.ySize * 2); // for visual + priority data
assert(spriteEntry.backgroundBuffer);
@@ -332,7 +354,8 @@ void SpritesMgr::showSprite(ScreenObjEntry *screenObj) {
}
// render this block
- _gfx->render_Block(x, y, width, height);
+ int16 upperY = y - height + 1;
+ _gfx->render_Block(x, upperY, width, height);
}
void SpritesMgr::showSprites(SpriteList &spriteList) {
@@ -394,7 +417,7 @@ void SpritesMgr::showObject(int16 viewNr) {
screenObj.yPos_prev = SCRIPT_HEIGHT - 1;
screenObj.yPos = screenObj.yPos_prev;
screenObj.priority = 15;
- screenObj.flags |= fFixedPriority;
+ screenObj.flags = fFixedPriority; // Original AGI did "| fFixedPriority" on uninitialized memory
screenObj.objectNr = 255; // ???
backgroundBuffer = (uint8 *)malloc(screenObj.xSize * screenObj.ySize * 2); // for visual + priority data
diff --git a/engines/agi/systemui.cpp b/engines/agi/systemui.cpp
index 2dd6629103..aeb1ded4a2 100644
--- a/engines/agi/systemui.cpp
+++ b/engines/agi/systemui.cpp
@@ -38,10 +38,14 @@ SystemUI::SystemUI(AgiEngine *vm, GfxMgr *gfx, TextMgr *text) {
_askForVerificationMouseLockedButtonNr = -1;
_askForVerificationMouseActiveButtonNr = -1;
+ clearSavedGameSlots();
+
_textStatusScore = "Score:%v3 of %v7";
_textStatusSoundOn = "Sound:on";
_textStatusSoundOff = "Sound:off";
+ _textEnterCommand = "Enter input\n\n";
+
_textPause = " Game paused.\nPress Enter to continue.";
_textPauseButton = nullptr;
@@ -212,6 +216,47 @@ const char *SystemUI::getInventoryTextReturnToGame() {
return _textInventoryReturnToGame;
}
+bool SystemUI::askForCommand(Common::String &commandText) {
+ // Let user enter the command (this was originally only available for Hercules rendering, we allow it everywhere)
+ bool previousEditState = _text->inputGetEditStatus();
+ byte previousEditCursor = _text->inputGetCursorChar();
+
+ _text->drawMessageBox(_textEnterCommand, 0, 36, true);
+
+ _text->inputEditOn();
+
+ _text->charPos_Push();
+ _text->charAttrib_Push();
+
+ _text->charPos_SetInsideWindow(2, 0);
+ _text->charAttrib_Set(15, 0);
+ _text->clearBlockInsideWindow(2, 0, 36, 0); // input line is supposed to be black
+ _text->inputSetCursorChar('_');
+
+ _text->stringSet(commandText.c_str()); // Set current command text (may be a command recall)
+
+ _vm->cycleInnerLoopActive(CYCLE_INNERLOOP_GETSTRING);
+ _text->stringEdit(35); // only allow up to 35 characters
+
+ _text->charAttrib_Pop();
+ _text->charPos_Pop();
+ _text->inputSetCursorChar(previousEditCursor);
+ if (!previousEditState) {
+ _text->inputEditOff();
+ }
+
+ _text->closeWindow();
+
+ if (!_text->stringWasEntered()) {
+ // User cancelled? exit now
+ return false;
+ }
+
+ commandText.clear();
+ commandText += (char *)_text->_inputString;
+ return true;
+}
+
int16 SystemUI::figureOutAutomaticSaveGameSlot(const char *automaticSaveDescription) {
int16 matchedGameSlotId = -1;
int16 freshGameSlotId = -1;
@@ -327,7 +372,7 @@ int16 SystemUI::askForRestoreGameSlot() {
int16 restoreGameSlotNr = -1;
// Fill saved game slot cache
- readSavedGameSlots(true, true); // filter empty/corrupt slots, but including auto-save slot
+ readSavedGameSlots(true, true); // filter empty/corrupt slots, but include auto-save slot
if (_savedGameArray.size() == 0) {
// no saved games
@@ -526,12 +571,12 @@ void SystemUI::readSavedGameSlots(bool filterNonexistant, bool withAutoSaveSlot)
SystemUISavedGameEntry savedGameEntry;
Common::String saveDescription;
uint32 saveDate = 0;
- uint16 saveTime = 0;
+ uint32 saveTime = 0;
bool saveIsValid = false;
int16 mostRecentSlotNr = -1;
uint32 mostRecentSlotSaveDate = 0;
- uint16 mostRecentSlotSaveTime = 0;
+ uint32 mostRecentSlotSaveTime = 0;
clearSavedGameSlots();
@@ -638,6 +683,9 @@ void SystemUI::readSavedGameSlots(bool filterNonexistant, bool withAutoSaveSlot)
void SystemUI::figureOutAutomaticSavedGameSlot(const char *automaticSaveDescription, int16 &matchedGameSlotId, int16 &freshGameSlotId) {
bool foundFresh = false;
+ matchedGameSlotId = -1;
+ freshGameSlotId = -1;
+
for (uint16 slotNr = 0; slotNr < _savedGameArray.size(); slotNr++) {
SystemUISavedGameEntry *savedGameEntry = &_savedGameArray[slotNr];
@@ -653,7 +701,7 @@ void SystemUI::figureOutAutomaticSavedGameSlot(const char *automaticSaveDescript
// no new slot found yet
if (!savedGameEntry->exists) {
// and current slot doesn't exist
- if (slotNr) {
+ if (savedGameEntry->slotId) {
// and slot is not the auto-save slot -> remember this slot
freshGameSlotId = savedGameEntry->slotId;
foundFresh = true;
@@ -746,21 +794,23 @@ bool SystemUI::askForVerification(const char *verifyText, const char *button1Tex
// Buttons enabled, calculate button coordinates
int16 msgBoxX = 0, msgBoxY = 0, msgBoxLowerY = 0;
int16 msgBoxWidth = 0, msgBoxHeight = 0;
+ int16 fontHeight = _gfx->getDisplayFontHeight();
+ int16 fontWidth = _gfx->getDisplayFontWidth();
_text->getMessageBoxInnerDisplayDimensions(msgBoxX, msgBoxY, msgBoxWidth, msgBoxHeight);
- // Adjust Y coordinate to lower edge
+ // Calculate lower Y
msgBoxLowerY = msgBoxY + (msgBoxHeight - 1);
buttonEntry.active = false;
if (button1Text) {
buttonEntry.text = button1Text;
- buttonEntry.textWidth = strlen(button1Text) * FONT_DISPLAY_WIDTH;
+ buttonEntry.textWidth = strlen(button1Text) * _gfx->getDisplayFontWidth();
buttonEntry.isDefault = true;
_buttonArray.push_back(buttonEntry);
}
if (button2Text) {
buttonEntry.text = button2Text;
- buttonEntry.textWidth = strlen(button2Text) * FONT_DISPLAY_WIDTH;
+ buttonEntry.textWidth = strlen(button2Text) * _gfx->getDisplayFontWidth();
buttonEntry.isDefault = false;
_buttonArray.push_back(buttonEntry);
}
@@ -768,37 +818,30 @@ bool SystemUI::askForVerification(const char *verifyText, const char *button1Tex
// Render-Mode specific calculations
switch (_vm->_renderMode) {
case Common::kRenderApple2GS:
- _buttonArray[0].rect = Common::Rect(14 + _buttonArray[0].textWidth, FONT_DISPLAY_HEIGHT + 6);
- _buttonArray[0].rect.moveTo(msgBoxX + 2, msgBoxLowerY - (8 + FONT_DISPLAY_HEIGHT + 2));
-
+ _buttonArray[0].rect = createRect(msgBoxX, +2, msgBoxLowerY - fontHeight, -(8 + 2), _buttonArray[0].textWidth, +14, fontHeight, +6);
+
if (_buttonArray.size() > 1) {
- int16 adjustedX = msgBoxX + msgBoxWidth - 10;
- _buttonArray[1].rect = Common::Rect(14 + _buttonArray[1].textWidth, FONT_DISPLAY_HEIGHT + 6);
- adjustedX -= _buttonArray[1].rect.width();
- _buttonArray[1].rect.moveTo(adjustedX, msgBoxLowerY - (8 + FONT_DISPLAY_HEIGHT + 2));
+ int16 adjustedX = msgBoxX + msgBoxWidth - _buttonArray[1].textWidth; // - 10;
+ _buttonArray[1].rect = createRect(adjustedX, -(14 + 10), _buttonArray[0].rect.top, 0, _buttonArray[1].textWidth, +14, fontHeight, +6);
}
break;
- case Common::kRenderAmiga:
- _buttonArray[0].rect = Common::Rect(4 + _buttonArray[0].textWidth + 4, 2 + FONT_DISPLAY_HEIGHT + 2);
- _buttonArray[0].rect.moveTo(msgBoxX, msgBoxLowerY - _buttonArray[0].rect.height());
+ case Common::kRenderAmiga: {
+ _buttonArray[0].rect = createRect(msgBoxX, 0, msgBoxLowerY - fontHeight, -(2 + 2), _buttonArray[0].textWidth, +(4 + 4), fontHeight, +(2 + 2));
if (_buttonArray.size() > 1) {
- int16 adjustedX = msgBoxX + msgBoxWidth;
- _buttonArray[1].rect = Common::Rect(4 + _buttonArray[1].textWidth + 4, 2 + FONT_DISPLAY_HEIGHT + 2);
- adjustedX -= _buttonArray[1].rect.width();
- _buttonArray[1].rect.moveTo(adjustedX, msgBoxLowerY - _buttonArray[1].rect.height());
+ int16 adjustedX = msgBoxX + msgBoxWidth - _buttonArray[1].textWidth;
+ _buttonArray[1].rect = createRect(adjustedX, -(4 + 4), _buttonArray[0].rect.top, 0, _buttonArray[1].textWidth, +(4 + 4), fontHeight, +(2 + 2));
}
break;
+ }
case Common::kRenderAtariST:
- _buttonArray[0].rect = Common::Rect(_buttonArray[0].textWidth, FONT_DISPLAY_HEIGHT);
- _buttonArray[0].rect.moveTo(msgBoxX + (5 * FONT_DISPLAY_WIDTH), msgBoxLowerY - FONT_DISPLAY_HEIGHT);
+ _buttonArray[0].rect = createRect(msgBoxX + (5 * fontWidth), 0, msgBoxLowerY - fontHeight, 0, _buttonArray[0].textWidth, 0, fontHeight, 0);
+
if (_buttonArray.size() > 1) {
- int16 adjustedX = msgBoxX + msgBoxWidth - (5 * FONT_DISPLAY_WIDTH);
- _buttonArray[1].rect = Common::Rect(_buttonArray[1].textWidth, FONT_DISPLAY_HEIGHT);
- adjustedX -= _buttonArray[1].rect.width();
- _buttonArray[1].rect.moveTo(adjustedX, msgBoxLowerY - _buttonArray[1].rect.height());
+ int16 adjustedX = msgBoxX + msgBoxWidth - (5 * fontWidth + _buttonArray[1].textWidth);
+ _buttonArray[1].rect = createRect(adjustedX, 0, _buttonArray[0].rect.top, 0, _buttonArray[1].textWidth, 0, fontHeight, 0);
}
break;
@@ -946,6 +989,25 @@ void SystemUI::askForVerificationKeyPress(uint16 newKey) {
}
}
+Common::Rect SystemUI::createRect(int16 x, int16 adjX, int16 y, int16 adjY, int16 width, int16 adjWidth, int16 height, int16 adjHeight) {
+ switch (_gfx->getUpscaledHires()) {
+ case DISPLAY_UPSCALED_DISABLED:
+ break;
+ case DISPLAY_UPSCALED_640x400:
+ adjX *= 2; adjY *= 2;
+ adjWidth *= 2; adjHeight *= 2;
+ break;
+ default:
+ assert(0);
+ break;
+ }
+ x += adjX; y += adjY;
+ width += adjWidth; height += adjHeight;
+ Common::Rect newRect(width, height);
+ newRect.moveTo(x, y);
+ return newRect;
+}
+
#define SYSTEMUI_BUTTONEDGE_APPLEIIGS_WIDTH 8
#define SYSTEMUI_BUTTONEDGE_APPLEIIGS_HEIGHT 5
@@ -993,20 +1055,20 @@ void SystemUI::drawButtonAppleIIgs(SystemUIButtonEntry *button) {
}
// draw base box for it
- _gfx->drawDisplayRect(button->rect.left, button->rect.bottom - 1, button->rect.width(), button->rect.height(), backgroundColor, false);
+ _gfx->drawDisplayRect(button->rect.left, button->rect.top, button->rect.width(), button->rect.height(), backgroundColor, false);
// draw inner lines
- _gfx->drawDisplayRect(button->rect.left + 1, button->rect.top - 1, button->rect.width() - 2, 1, 0, false); // upper horizontal
- _gfx->drawDisplayRect(button->rect.left - 2, button->rect.bottom - 2, 2, button->rect.height() - 2, 0, false); // left vertical
- _gfx->drawDisplayRect(button->rect.right, button->rect.bottom - 2, 2, button->rect.height() - 2, 0, false); // right vertical
- _gfx->drawDisplayRect(button->rect.left + 1, button->rect.bottom, button->rect.width() - 2, 1, 0, false); // lower horizontal
+ _gfx->drawDisplayRect(button->rect.left, +1, button->rect.top, -1, button->rect.width(), -2, 0, 1, 0, false); // lower horizontal
+ _gfx->drawDisplayRect(button->rect.left, -2, button->rect.top, +1, 0, 2, button->rect.height(), -2, 0, false); // left vertical
+ _gfx->drawDisplayRect(button->rect.right, 0, button->rect.top, +1, 0, 2, button->rect.height(), -2, 0, false); // right vertical
+ _gfx->drawDisplayRect(button->rect.left, +1, button->rect.bottom, 0, button->rect.width(), -2, 0, 1, 0, false); // upper horizontal
if (button->isDefault) {
// draw outer lines
- _gfx->drawDisplayRect(button->rect.left, button->rect.top - 3, button->rect.width(), 1, 0, false); // upper horizontal
- _gfx->drawDisplayRect(button->rect.left - 5, button->rect.bottom - 2, 2, button->rect.height() - 2, 0, false); // left vertical
- _gfx->drawDisplayRect(button->rect.right + 3, button->rect.bottom - 2, 2, button->rect.height() - 2, 0, false); // right vertical
- _gfx->drawDisplayRect(button->rect.left, button->rect.bottom + 2, button->rect.width(), 1, 0, false); // lower horizontal
+ _gfx->drawDisplayRect(button->rect.left, 0, button->rect.top, -3, button->rect.width(), 0, 0, 1, 0, false); // upper horizontal
+ _gfx->drawDisplayRect(button->rect.left, -5, button->rect.top, +2, 0, 2, button->rect.height(), -2, 0, false); // left vertical
+ _gfx->drawDisplayRect(button->rect.right, +3, button->rect.top, +2, 0, 2, button->rect.height(), -2, 0, false); // right vertical
+ _gfx->drawDisplayRect(button->rect.left, 0, button->rect.bottom, +2, button->rect.width(), 0, 0, 1, 0, false); // lower horizontal
if (button->active)
edgeBitmap = buttonEdgeAppleIIgsDefaultActive;
@@ -1021,18 +1083,18 @@ void SystemUI::drawButtonAppleIIgs(SystemUIButtonEntry *button) {
}
// draw edge graphics
- drawButtonAppleIIgsEdgePixels(button->rect.left - 5, button->rect.top - 3, edgeBitmap, false, false);
- drawButtonAppleIIgsEdgePixels(button->rect.right + 4, button->rect.top - 3, edgeBitmap, true, false);
- drawButtonAppleIIgsEdgePixels(button->rect.left - 5, button->rect.bottom + 2, edgeBitmap, false, true);
- drawButtonAppleIIgsEdgePixels(button->rect.right + 4, button->rect.bottom + 2, edgeBitmap, true, true);
+ drawButtonAppleIIgsEdgePixels(button->rect.left, -5, button->rect.top, -3, edgeBitmap, false, false);
+ drawButtonAppleIIgsEdgePixels(button->rect.right, +4, button->rect.top, -3, edgeBitmap, true, false);
+ drawButtonAppleIIgsEdgePixels(button->rect.left, -5, button->rect.bottom, +2, edgeBitmap, false, true);
+ drawButtonAppleIIgsEdgePixels(button->rect.right, +4, button->rect.bottom, +2, edgeBitmap, true, true);
// Button text
- _gfx->drawStringOnDisplay(button->rect.left + 7, button->rect.top + 3, button->text, foregroundColor, backgroundColor);
+ _gfx->drawStringOnDisplay(button->rect.left, +7, button->rect.top, +3, button->text, foregroundColor, backgroundColor);
- _gfx->copyDisplayRectToScreen(button->rect.left - 5, button->rect.top - 3, button->rect.width() + 10, button->rect.height() + 6);
+ _gfx->copyDisplayRectToScreen(button->rect.left, -5, button->rect.top, -3, button->rect.width(), +10, button->rect.height(), +6);
}
-void SystemUI::drawButtonAppleIIgsEdgePixels(int16 x, int16 y, byte *edgeBitmap, bool mirrored, bool upsideDown) {
+void SystemUI::drawButtonAppleIIgsEdgePixels(int16 x, int16 adjX, int16 y, int16 adjY, byte *edgeBitmap, bool mirrored, bool upsideDown) {
int8 directionY = upsideDown ? -1 : +1;
int8 directionX = mirrored ? -1 : +1;
int8 curY = 0;
@@ -1050,9 +1112,9 @@ void SystemUI::drawButtonAppleIIgsEdgePixels(int16 x, int16 y, byte *edgeBitmap,
while (widthLeft) {
if (curBitmapByte & curBitmapBit) {
- _gfx->putPixelOnDisplay(x + curX, y + curY, 0);
+ _gfx->putPixelOnDisplay(x, adjX + curX, y, adjY + curY, 0);
} else {
- _gfx->putPixelOnDisplay(x + curX, y + curY, 15);
+ _gfx->putPixelOnDisplay(x, adjX + curX, y, adjY + curY, 15);
}
curBitmapBit = curBitmapBit >> 1;
@@ -1087,12 +1149,11 @@ void SystemUI::drawButtonAmiga(SystemUIButtonEntry *button) {
}
// draw base box for it
- _gfx->drawDisplayRect(button->rect.left, button->rect.bottom - 1, button->rect.width(), button->rect.height(), backgroundColor, false);
+ _gfx->drawDisplayRect(button->rect.left, button->rect.top, button->rect.width(), button->rect.height(), backgroundColor, false);
// Button text
- _gfx->drawStringOnDisplay(button->rect.left + 4, button->rect.top + 2, button->text, foregroundColor, backgroundColor);
+ _gfx->drawStringOnDisplay(button->rect.left, +4, button->rect.top, +2, button->text, foregroundColor, backgroundColor);
- // draw base box for it
_gfx->copyDisplayRectToScreen(button->rect.left, button->rect.top, button->rect.width(), button->rect.height());
}
diff --git a/engines/agi/systemui.h b/engines/agi/systemui.h
index ceb78935eb..283de8794c 100644
--- a/engines/agi/systemui.h
+++ b/engines/agi/systemui.h
@@ -77,6 +77,8 @@ public:
const char *getInventoryTextSelectItems();
const char *getInventoryTextReturnToGame();
+ bool askForCommand(Common::String &commandText);
+
int16 figureOutAutomaticSaveGameSlot(const char *automaticSaveDescription);
int16 figureOutAutomaticRestoreGameSlot(const char *automaticSaveDescription);
@@ -107,9 +109,12 @@ private:
private:
SystemUIButtonArray _buttonArray;
+ Common::Rect createRect(int16 x, int16 adjX, int16 y, int16 adjY, int16 width, int16 adjWidth, int16 height, int16 adjHeight);
+ //void moveRect(int16 x, int16 adjX, int16 y, int16 adjY);
+
void drawButton(SystemUIButtonEntry *button);
void drawButtonAppleIIgs(SystemUIButtonEntry *buttonEntry);
- void drawButtonAppleIIgsEdgePixels(int16 x, int16 y, byte *edgeBitmap, bool mirrored, bool upsideDown);
+ void drawButtonAppleIIgsEdgePixels(int16 x, int16 adjX, int16 y, int16 adjY, byte *edgeBitmap, bool mirrored, bool upsideDown);
void drawButtonAmiga(SystemUIButtonEntry *buttonEntry);
void drawButtonAtariST(SystemUIButtonEntry *buttonEntry);
@@ -127,6 +132,8 @@ private:
const char *_textStatusSoundOn;
const char *_textStatusSoundOff;
+ const char *_textEnterCommand;
+
const char *_textPause;
const char *_textPauseButton;
const char *_textRestart;
diff --git a/engines/agi/text.cpp b/engines/agi/text.cpp
index 965de69335..0cacce2421 100644
--- a/engines/agi/text.cpp
+++ b/engines/agi/text.cpp
@@ -20,6 +20,7 @@
*
*/
+#include "common/config-manager.h"
#include "agi/agi.h"
#include "agi/sprite.h" // for commit_both()
#include "agi/graphics.h"
@@ -66,6 +67,7 @@ TextMgr::TextMgr(AgiEngine *vm, Words *words, GfxMgr *gfx) {
_inputStringRow = 0;
_inputStringColumn = 0;
+ _inputStringEntered = false;
_inputStringMaxLen = 0;
_inputStringCursorPos = 0;
_inputString[0] = 0;
@@ -73,6 +75,12 @@ TextMgr::TextMgr(AgiEngine *vm, Words *words, GfxMgr *gfx) {
configureScreen(2);
_messageBoxCancelled = false;
+
+ _optionCommandPromptWindow = false;
+
+ if (ConfMan.getBool("commandpromptwindow")) {
+ _optionCommandPromptWindow = true;
+ }
}
TextMgr::~TextMgr() {
@@ -87,7 +95,7 @@ void TextMgr::configureScreen(uint16 row_Min) {
_window_Row_Max = row_Min + 21;
// forward data to GfxMgr as well
- _gfx->setRenderStartOffset(row_Min * FONT_DISPLAY_HEIGHT);
+ _gfx->setRenderStartOffset(row_Min * FONT_VISUAL_HEIGHT);
}
uint16 TextMgr::getWindowRowMin() {
return _window_Row_Min;
@@ -169,7 +177,7 @@ void TextMgr::charAttrib_Set(byte foreground, byte background) {
if (background) {
_textAttrib.combinedForeground = 3;
_textAttrib.combinedBackground = 8; // enable invert of colors
- } else if (foreground > 14) {
+ } else {
if (foreground > 14) {
_textAttrib.combinedForeground = 3;
} else {
@@ -178,6 +186,16 @@ void TextMgr::charAttrib_Set(byte foreground, byte background) {
_textAttrib.combinedBackground = 0;
}
break;
+ case Common::kRenderHercA:
+ case Common::kRenderHercG:
+ if (background) {
+ _textAttrib.combinedForeground = 0;
+ _textAttrib.combinedBackground = 1;
+ } else {
+ _textAttrib.combinedForeground = 1;
+ _textAttrib.combinedBackground = 0;
+ }
+ break;
default:
// EGA-handling:
if (background) {
@@ -465,7 +483,8 @@ void TextMgr::drawMessageBox(const char *textPtr, int16 forcedHeight, int16 want
_messageState.backgroundSize_Width = (_messageState.textSize_Width * FONT_VISUAL_WIDTH) + 10;
_messageState.backgroundSize_Height = (_messageState.textSize_Height * FONT_VISUAL_HEIGHT) + 10;
_messageState.backgroundPos_x = (_messageState.textPos.column * FONT_VISUAL_WIDTH) - 5;
- _messageState.backgroundPos_y = (_messageState.textPos_Edge.row - _window_Row_Min + 1) * FONT_VISUAL_HEIGHT + 4;
+ _messageState.backgroundPos_y = (startingRow * FONT_VISUAL_HEIGHT) - 5;
+ // original AGI used lowerY here, calculated using (_messageState.textPos_Edge.row - _window_Row_Min + 1) * FONT_VISUAL_HEIGHT + 4;
// Hardcoded colors: white background and red lines
_gfx->drawBox(_messageState.backgroundPos_x, _messageState.backgroundPos_y, _messageState.backgroundSize_Width, _messageState.backgroundSize_Height, 15, 4);
@@ -486,10 +505,11 @@ void TextMgr::getMessageBoxInnerDisplayDimensions(int16 &x, int16 &y, int16 &wid
if (!_messageState.window_Active)
return;
- y = _messageState.textPos.row * FONT_DISPLAY_HEIGHT;
- x = _messageState.textPos.column * FONT_DISPLAY_WIDTH;
- width = _messageState.textSize_Width * FONT_DISPLAY_WIDTH;
- height = _messageState.textSize_Height * FONT_DISPLAY_HEIGHT;
+ y = _messageState.textPos.row;
+ x = _messageState.textPos.column;
+ width = _messageState.textSize_Width;
+ height = _messageState.textSize_Height;
+ _gfx->translateFontRectToDisplayScreen(x, y, width, height);
}
bool TextMgr::isMouseWithinMessageBox() {
@@ -498,10 +518,10 @@ bool TextMgr::isMouseWithinMessageBox() {
int16 mouseX = _vm->_mouse.pos.x;
if (_messageState.window_Active) {
- _vm->adjustPosToGameScreen(mouseX, mouseY);
+ _gfx->translateDisplayPosToGameScreen(mouseX, mouseY);
- if ((mouseX >= _messageState.backgroundPos_x) && (mouseX <= (_messageState.backgroundPos_x + _messageState.backgroundSize_Width))) {
- if ((mouseY >= _messageState.backgroundPos_y - _messageState.backgroundSize_Height) && (mouseY <= (_messageState.backgroundPos_y))) {
+ if ((mouseX >= _messageState.backgroundPos_x) && (mouseX < (_messageState.backgroundPos_x + _messageState.backgroundSize_Width))) {
+ if ((mouseY >= _messageState.backgroundPos_y) && (mouseY < (_messageState.backgroundPos_y + _messageState.backgroundSize_Height))) {
return true;
}
}
@@ -580,12 +600,12 @@ void TextMgr::clearBlock(int16 row_Upper, int16 column_Upper, int16 row_Lower, i
charPos_Clip(row_Upper, column_Upper);
charPos_Clip(row_Lower, column_Lower);
- int16 x = column_Upper * FONT_DISPLAY_WIDTH;
- int16 y = row_Upper * FONT_DISPLAY_HEIGHT;
- int16 width = (column_Lower + 1 - column_Upper) * FONT_DISPLAY_WIDTH;
- int16 height = (row_Lower + 1 - row_Upper) * FONT_DISPLAY_HEIGHT;
+ int16 x = column_Upper;
+ int16 y = row_Upper;
+ int16 width = (column_Lower + 1 - column_Upper);
+ int16 height = (row_Lower + 1 - row_Upper);
+ _gfx->translateFontRectToDisplayScreen(x, y, width, height);
- y = y + height - 1; // drawDisplayRect wants lower Y-coordinate
_gfx->drawDisplayRect(x, y, width, height, color);
}
@@ -658,6 +678,30 @@ void TextMgr::promptKeyPress(uint16 newKey) {
int16 maxChars = 0;
int16 scriptsInputLen = _vm->getVar(VM_VAR_MAX_INPUT_CHARACTERS);
+ bool acceptableInput = false;
+
+ // FEATURE: Sierra didn't check for valid characters (filtered out umlauts etc.)
+ // In text-mode this sort of worked at least with the DOS interpreter
+ // but as soon as invalid characters were used in graphics mode they weren't properly shown
+ switch (_vm->getLanguage()) {
+ case Common::RU_RUS:
+ if (newKey >= 0x20)
+ acceptableInput = true;
+ break;
+ default:
+ if ((newKey >= 0x20) && (newKey <= 0x7f))
+ acceptableInput = true;
+ break;
+ }
+
+ if (_optionCommandPromptWindow) {
+ // Forward to command prompt window, using last command
+ if (acceptableInput) {
+ promptCommandWindow(false, newKey);
+ }
+ return;
+ }
+
if (_messageState.dialogue_Open) {
maxChars = TEXT_STRING_MAX_SIZE - 4;
} else {
@@ -702,22 +746,6 @@ void TextMgr::promptKeyPress(uint16 newKey) {
}
default:
if (maxChars > _promptCursorPos) {
- bool acceptableInput = false;
-
- // FEATURE: Sierra didn't check for valid characters (filtered out umlauts etc.)
- // In text-mode this sort of worked at least with the DOS interpreter
- // but as soon as invalid characters were used in graphics mode they weren't properly shown
- switch (_vm->getLanguage()) {
- case Common::RU_RUS:
- if (newKey >= 0x20)
- acceptableInput = true;
- break;
- default:
- if ((newKey >= 0x20) && (newKey <= 0x7f))
- acceptableInput = true;
- break;
- }
-
if (acceptableInput) {
_prompt[_promptCursorPos] = newKey;
_promptCursorPos++;
@@ -734,6 +762,11 @@ void TextMgr::promptKeyPress(uint16 newKey) {
}
void TextMgr::promptCancelLine() {
+ if (_optionCommandPromptWindow) {
+ // Abort, in case command prompt window is active
+ return;
+ }
+
while (_promptCursorPos) {
promptKeyPress(0x08); // Backspace until prompt is empty
}
@@ -742,6 +775,12 @@ void TextMgr::promptCancelLine() {
void TextMgr::promptEchoLine() {
int16 previousLen = strlen((char *)_promptPrevious);
+ if (_optionCommandPromptWindow) {
+ // Forward to command prompt window, using last command
+ promptCommandWindow(true, 0);
+ return;
+ }
+
if (_promptCursorPos < previousLen) {
inputEditOn();
@@ -758,6 +797,11 @@ void TextMgr::promptRedraw() {
char *textPtr = nullptr;
if (_promptEnabled) {
+ if (_optionCommandPromptWindow) {
+ // Abort, in case command prompt window is active
+ return;
+ }
+
inputEditOn();
clearLine(_promptRow, _textAttrib.background);
charPos_Set(_promptRow, 0);
@@ -775,6 +819,10 @@ void TextMgr::promptRedraw() {
// for AGI1
void TextMgr::promptClear() {
+ if (_optionCommandPromptWindow) {
+ // Abort, in case command prompt window is active
+ return;
+ }
clearLine(_promptRow, _textAttrib.background);
}
@@ -784,6 +832,35 @@ void TextMgr::promptRememberForAutoComplete(bool entered) {
#endif
}
+void TextMgr::promptCommandWindow(bool recallLastCommand, uint16 newKey) {
+ Common::String commandText;
+
+ if (recallLastCommand) {
+ commandText += Common::String((char *)_promptPrevious);
+ }
+ if (newKey) {
+ if (newKey != ' ') {
+ // Only add char, when it's not a space.
+ // Original AGI did not filter space, but it makes no sense to start with a space.
+ // Space would get filtered anyway during dictionary parsing.
+ commandText += newKey;
+ }
+ }
+
+ if (_systemUI->askForCommand(commandText)) {
+ if (commandText.size()) {
+ // Something actually was entered?
+ strncpy((char *)&_prompt, commandText.c_str(), sizeof(_prompt));
+ promptRememberForAutoComplete(true);
+ memcpy(&_promptPrevious, &_prompt, sizeof(_prompt));
+ // parse text
+ _vm->_words->parseUsingDictionary((char *)&_prompt);
+
+ _prompt[0] = 0;
+ }
+ }
+}
+
bool TextMgr::stringWasEntered() {
return _inputStringEntered;
}
@@ -941,8 +1018,18 @@ char *TextMgr::stringWordWrap(const char *originalText, int16 maxWidth, int16 *c
//memset(resultWrappedBuffer, 0, sizeof(resultWrappedBuffer)); for debugging
+ // Good testcases:
+ // King's Quest 1 intro: the scrolling text is filled up with spaces, so that old lines are erased
+ // Apple IIgs restart system UI: spaces used to make the window larger
+ // Gold Rush Stagecoach path room 60: " Lake Michigan!", with max length 9 -> should get split into " Lake" / "Michigan!"
+
while (originalText[curReadPos]) {
// Try to find out length of next word
+
+ // If first character is a space, skip it, so that we process at least this space
+ if (originalText[curReadPos] == ' ')
+ curReadPos++;
+
while (originalText[curReadPos]) {
if (originalText[curReadPos] == ' ')
break;
@@ -957,6 +1044,15 @@ char *TextMgr::stringWordWrap(const char *originalText, int16 maxWidth, int16 *c
if (wordLen >= lineWidthLeft) {
// Not enough space left
+
+ // If first character right after the new line is a space, skip over it
+ if (wordLen) {
+ if (originalText[wordStartPos] == ' ') {
+ wordStartPos++;
+ wordLen--;
+ }
+ }
+
if (wordLen > maxWidth) {
// Word way too long, split it in half
curReadPos = curReadPos - (wordLen - maxWidth);
@@ -973,14 +1069,6 @@ char *TextMgr::stringWordWrap(const char *originalText, int16 maxWidth, int16 *c
// Reached absolute maximum? -> exit now
if (boxHeight >= HEIGHT_MAX)
break;
-
- // If first character right after the new line is a space, skip over it
- if (wordLen) {
- if (originalText[wordStartPos] == ' ') {
- wordStartPos++;
- wordLen--;
- }
- }
}
// Copy current word over
@@ -1005,10 +1093,6 @@ char *TextMgr::stringWordWrap(const char *originalText, int16 maxWidth, int16 *c
}
wordStartPos = curReadPos;
-
- // Last word ended with a space, skip this space for reading the next word
- if (wordEndChar == ' ')
- curReadPos++;
}
resultWrappedBuffer[curWritePos] = 0;
diff --git a/engines/agi/text.h b/engines/agi/text.h
index 72d012b917..f0aeab7762 100644
--- a/engines/agi/text.h
+++ b/engines/agi/text.h
@@ -55,7 +55,7 @@ struct MessageState_Struct {
uint16 printed_Height;
int16 backgroundPos_x;
- int16 backgroundPos_y;
+ int16 backgroundPos_y; // original AGI used lowerY here, we use upperY so that upscaling is easier
int16 backgroundSize_Width;
int16 backgroundSize_Height;
};
@@ -163,6 +163,8 @@ public:
bool _inputEditEnabled;
byte _inputCursorChar;
+ bool _optionCommandPromptWindow;
+
bool _promptEnabled;
int16 _promptRow;
int16 _promptCursorPos;
@@ -189,6 +191,8 @@ public:
void promptClear(); // for AGI1
void promptRememberForAutoComplete(bool entered = false); // for auto-completion
+ void promptCommandWindow(bool recallLastCommand, uint16 newKey);
+
int16 _inputStringRow;
int16 _inputStringColumn;
bool _inputStringEntered;
diff --git a/engines/agi/view.cpp b/engines/agi/view.cpp
index ac3e60ee6d..a13e40e60d 100644
--- a/engines/agi/view.cpp
+++ b/engines/agi/view.cpp
@@ -111,7 +111,6 @@ int AgiEngine::decodeView(byte *resourceData, uint16 resourceSize, int16 viewNr)
byte *celCompressedData = nullptr;
uint16 celCompressedSize = 0;
-// byte *rawBitmap = nullptr;
debugC(5, kDebugLevelResources, "decode_view(%d)", viewNr);
@@ -431,14 +430,28 @@ void AgiEngine::unloadView(int16 viewNr) {
* @param viewNr number of AGI view resource
*/
void AgiEngine::setView(ScreenObjEntry *screenObj, int16 viewNr) {
- screenObj->viewData = &_game.views[viewNr];
+ if (!(_game.dirView[viewNr].flags & RES_LOADED)) {
+ // View resource currently not loaded, this is probably a game bug
+ // Load the resource now to fix the issue, and give out a warning
+ // This happens in at least Larry 1 for Apple IIgs right after getting beaten up by taxi driver
+ // Original interpreter bombs out in this situation saying "view not loaded, Press ESC to quit"
+ warning("setView() called on screen object %d to use view %d, but view not loaded", screenObj->objectNr, viewNr);
+ warning("probably game script bug, trying to load view into memory");
+ if (agiLoadResource(RESOURCETYPE_VIEW, viewNr) != errOK) {
+ // loading failed, we better error() out now
+ error("setView() called to set view %d for screen object %d, which is not loaded atm and loading failed", viewNr, screenObj->objectNr);
+ return;
+ };
+ }
+
+ screenObj->viewResource = &_game.views[viewNr];
screenObj->currentViewNr = viewNr;
- screenObj->loopCount = screenObj->viewData->loopCount;
+ screenObj->loopCount = screenObj->viewResource->loopCount;
screenObj->viewReplaced = true;
if (getVersion() < 0x2000) {
- screenObj->stepSize = screenObj->viewData->headerStepSize;
- screenObj->cycleTime = screenObj->viewData->headerCycleTime;
+ screenObj->stepSize = screenObj->viewResource->headerStepSize;
+ screenObj->cycleTime = screenObj->viewResource->headerCycleTime;
screenObj->cycleTimeCount = 0;
}
if (screenObj->currentLoopNr >= screenObj->loopCount) {
@@ -454,7 +467,11 @@ void AgiEngine::setView(ScreenObjEntry *screenObj, int16 viewNr) {
* @param loopNr number of loop
*/
void AgiEngine::setLoop(ScreenObjEntry *screenObj, int16 loopNr) {
- assert(screenObj->viewData != NULL);
+ if (!(_game.dirView[screenObj->currentViewNr].flags & RES_LOADED)) {
+ error("setLoop() called on screen object %d, which has no loaded view resource assigned to it", screenObj->objectNr);
+ return;
+ }
+ assert(screenObj->viewResource);
if (screenObj->loopCount == 0) {
warning("setLoop() called on screen object %d, which has no loops (view %d)", screenObj->objectNr, screenObj->currentViewNr);
@@ -465,7 +482,11 @@ void AgiEngine::setLoop(ScreenObjEntry *screenObj, int16 loopNr) {
// requested loop not existant
// instead of error()ing out, we instead clip it
// At least required for possibly Manhunter 1 according to previous comment when leaving the arcade machine
- // TODO: check MH1
+ // TODO: Check MH1
+ // TODO: This causes an issue in KQ1, when bowing to the king in room 53
+ // Ego will face away from the king, because the scripts set the loop first and then the view
+ // Loop is corrected by us, because at that time it's invalid. Was already present in 1.7.0
+ // We should probably script-patch it out.
int16 requestedLoopNr = loopNr;
loopNr = screenObj->loopCount - 1;
@@ -493,7 +514,16 @@ void AgiEngine::setLoop(ScreenObjEntry *screenObj, int16 loopNr) {
* @param celNr number of cel
*/
void AgiEngine::setCel(ScreenObjEntry *screenObj, int16 celNr) {
- assert(screenObj->viewData != NULL);
+ if (!(_game.dirView[screenObj->currentViewNr].flags & RES_LOADED)) {
+ error("setCel() called on screen object %d, which has no loaded view resource assigned to it", screenObj->objectNr);
+ return;
+ }
+ assert(screenObj->viewResource);
+
+ if (screenObj->loopCount == 0) {
+ warning("setLoop() called on screen object %d, which has no loops (view %d)", screenObj->objectNr, screenObj->currentViewNr);
+ return;
+ }
AgiViewLoop *curViewLoop = &_game.views[screenObj->currentViewNr].loop[screenObj->currentLoopNr];
diff --git a/engines/agi/view.h b/engines/agi/view.h
index 04021260a3..e59916da78 100644
--- a/engines/agi/view.h
+++ b/engines/agi/view.h
@@ -47,10 +47,6 @@ struct AgiView {
byte *description;
int16 loopCount;
AgiViewLoop *loop;
-
- //struct ViewLoop *loop;
- //bool agi256_2;
- //byte *resourceData;
};
enum MotionType {
@@ -98,7 +94,7 @@ struct ScreenObjEntry {
int16 yPos;
uint8 currentViewNr;
bool viewReplaced;
- struct AgiView *viewData;
+ struct AgiView *viewResource;
uint8 currentLoopNr;
uint8 loopCount;
struct AgiViewLoop *loopData;
diff --git a/engines/agi/words.cpp b/engines/agi/words.cpp
index 32fa4cbff4..7072f003c2 100644
--- a/engines/agi/words.cpp
+++ b/engines/agi/words.cpp
@@ -293,7 +293,7 @@ int16 Words::findWordInDictionary(const Common::String &userInputLowcased, uint1
return wordId;
}
-void Words::parseUsingDictionary(char *rawUserInput) {
+void Words::parseUsingDictionary(const char *rawUserInput) {
Common::String userInput;
Common::String userInputLowcased;
const char *userInputPtr = nullptr;
diff --git a/engines/agi/words.h b/engines/agi/words.h
index c7bf4829c3..96dafae275 100644
--- a/engines/agi/words.h
+++ b/engines/agi/words.h
@@ -57,7 +57,7 @@ public:
void unloadDictionary();
void clearEgoWords();
- void parseUsingDictionary(char *rawUserInput);
+ void parseUsingDictionary(const char *rawUserInput);
private:
void cleanUpInput(const char *userInput, Common::String &cleanInput);
diff --git a/engines/agos/configure.engine b/engines/agos/configure.engine
index 3ae1fb16f2..cd7fcf9d78 100644
--- a/engines/agos/configure.engine
+++ b/engines/agos/configure.engine
@@ -1,4 +1,4 @@
# This file is included from the main "configure" script
# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps]
add_engine agos "AGOS" yes "agos2" "AGOS 1 games"
-add_engine agos2 "AGOS 2 games" yes
+add_engine agos2 "AGOS 2 games" yes "" "" "highres"
diff --git a/engines/agos/detection.cpp b/engines/agos/detection.cpp
index 94446159b9..2c89522089 100644
--- a/engines/agos/detection.cpp
+++ b/engines/agos/detection.cpp
@@ -94,13 +94,13 @@ using namespace AGOS;
class AgosMetaEngine : public AdvancedMetaEngine {
public:
AgosMetaEngine() : AdvancedMetaEngine(AGOS::gameDescriptions, sizeof(AGOS::AGOSGameDescription), agosGames) {
- _guioptions = GUIO1(GUIO_NOLAUNCHLOAD);
+ _guiOptions = GUIO1(GUIO_NOLAUNCHLOAD);
_maxScanDepth = 2;
_directoryGlobs = directoryGlobs;
}
- virtual GameDescriptor findGame(const char *gameid) const {
- return Engines::findGameID(gameid, _gameids, obsoleteGameIDsTable);
+ virtual GameDescriptor findGame(const char *gameId) const {
+ return Engines::findGameID(gameId, _gameIds, obsoleteGameIDsTable);
}
virtual const char *getName() const {
@@ -186,7 +186,6 @@ SaveStateList AgosMetaEngine::listSaves(const char *target) const {
pattern += ".###";
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) {
@@ -203,6 +202,8 @@ SaveStateList AgosMetaEngine::listSaves(const char *target) const {
}
}
+ // Sort saves based on slot number.
+ Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator());
return saveList;
}
diff --git a/engines/agos/sound.cpp b/engines/agos/sound.cpp
index 762f60bd91..13b5bf761c 100644
--- a/engines/agos/sound.cpp
+++ b/engines/agos/sound.cpp
@@ -217,7 +217,7 @@ static void convertVolume(int &vol) {
}
static void convertPan(int &pan) {
- // DirectSound was orginally used, which specifies volume
+ // DirectSound was originally used, which specifies volume
// and panning differently than ScummVM does, using a logarithmic scale
// rather than a linear one.
//
diff --git a/engines/avalanche/POTFILES b/engines/avalanche/POTFILES
new file mode 100644
index 0000000000..5b0bb910ed
--- /dev/null
+++ b/engines/avalanche/POTFILES
@@ -0,0 +1 @@
+engines/avalanche/parser.cpp
diff --git a/engines/avalanche/configure.engine b/engines/avalanche/configure.engine
index 28d6a558db..9b913ff053 100644
--- a/engines/avalanche/configure.engine
+++ b/engines/avalanche/configure.engine
@@ -1,3 +1,3 @@
# This file is included from the main "configure" script
# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps]
-add_engine avalanche "Lord Avalot d'Argent" no
+add_engine avalanche "Lord Avalot d'Argent" no "" "" "highres"
diff --git a/engines/avalanche/detection.cpp b/engines/avalanche/detection.cpp
index 484d148161..e35c5d2cac 100644
--- a/engines/avalanche/detection.cpp
+++ b/engines/avalanche/detection.cpp
@@ -40,7 +40,7 @@ uint32 AvalancheEngine::getFeatures() const {
}
const char *AvalancheEngine::getGameId() const {
- return _gameDescription->desc.gameid;
+ return _gameDescription->desc.gameId;
}
static const PlainGameDescriptor avalancheGames[] = {
@@ -110,7 +110,6 @@ SaveStateList AvalancheMetaEngine::listSaves(const char *target) const {
pattern += ".###";
filenames = saveFileMan->listSavefiles(pattern);
- sort(filenames.begin(), filenames.end()); // Sort (hopefully ensuring we are sorted numerically..)
SaveStateList saveList;
for (Common::StringArray::const_iterator filename = filenames.begin(); filename != filenames.end(); ++filename) {
@@ -152,6 +151,8 @@ SaveStateList AvalancheMetaEngine::listSaves(const char *target) const {
}
}
+ // Sort saves based on slot number.
+ Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator());
return saveList;
}
diff --git a/engines/avalanche/parser.cpp b/engines/avalanche/parser.cpp
index 220186ca5e..7c6d254099 100644
--- a/engines/avalanche/parser.cpp
+++ b/engines/avalanche/parser.cpp
@@ -30,6 +30,7 @@
#include "avalanche/nim.h"
#include "gui/saveload.h"
+#include "common/translation.h"
namespace Avalanche {
@@ -1883,7 +1884,7 @@ void Parser::doThat() {
break;
case kVerbCodeLoad: {
- GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser("Restore game:", "Restore", false);
+ GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser(_("Restore game:"), _("Restore"), false);
int16 savegameId = dialog->runModalWithCurrentTarget();
delete dialog;
@@ -1895,7 +1896,7 @@ void Parser::doThat() {
}
break;
case kVerbCodeSave: {
- GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser("Save game:", "Save", true);
+ GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser(_("Save game:"), _("Save"), true);
int16 savegameId = dialog->runModalWithCurrentTarget();
Common::String savegameDescription = dialog->getResultString();
delete dialog;
diff --git a/engines/bbvs/bbvs.cpp b/engines/bbvs/bbvs.cpp
index d40d5e482f..6ae663479d 100644
--- a/engines/bbvs/bbvs.cpp
+++ b/engines/bbvs/bbvs.cpp
@@ -137,6 +137,21 @@ BbvsEngine::~BbvsEngine() {
}
void BbvsEngine::newGame() {
+ memset(_easterEggInput, 0, sizeof(_easterEggInput));
+ _gameTicks = 0;
+ _playVideoNumber = 0;
+ memset(_inventoryItemStatus, 0, sizeof(_inventoryItemStatus));
+ memset(_gameVars, 0, sizeof(_gameVars));
+ memset(_sceneVisited, 0, sizeof(_sceneVisited));
+
+ _mouseX = 160;
+ _mouseY = 120;
+ _mouseButtons = 0;
+
+ _currVerbNum = kVerbLook;
+ _currTalkObjectIndex = -1;
+ _currSceneNum = 0;
+
_currInventoryItem = -1;
_newSceneNum = 32;
}
@@ -162,24 +177,10 @@ Common::Error BbvsEngine::run() {
_sound = new SoundMan();
allocSnapshot();
- memset(_easterEggInput, 0, sizeof(_easterEggInput));
- _gameTicks = 0;
- _playVideoNumber = 0;
- _bootSaveSlot = -1;
-
- memset(_inventoryItemStatus, 0, sizeof(_inventoryItemStatus));
- memset(_gameVars, 0, sizeof(_gameVars));
- memset(_sceneVisited, 0, sizeof(_sceneVisited));
-
- _mouseX = 160;
- _mouseY = 120;
- _mouseButtons = 0;
+ newGame();
- _currVerbNum = kVerbLook;
- _currInventoryItem = -1;
- _currTalkObjectIndex = -1;
- _currSceneNum = 0;
+ _bootSaveSlot = -1;
_newSceneNum = 31;
if (ConfMan.hasKey("save_slot"))
diff --git a/engines/bbvs/detection.cpp b/engines/bbvs/detection.cpp
index eb894e9f13..7c0045ee73 100644
--- a/engines/bbvs/detection.cpp
+++ b/engines/bbvs/detection.cpp
@@ -43,7 +43,7 @@ static const ADGameDescription gameDescriptions[] = {
AD_ENTRY1s("vspr0001.vnm", "7ffe9b9e7ca322db1d48e86f5130578e", 1166628),
Common::EN_ANY,
Common::kPlatformWindows,
- ADGF_NO_FLAGS | ADGF_TESTING,
+ ADGF_NO_FLAGS,
GUIO0()
},
{
@@ -52,7 +52,7 @@ static const ADGameDescription gameDescriptions[] = {
AD_ENTRY1s("vspr0001.vnm", "91c76b1048f93208cd7b1a05ebccb408", 1176976),
Common::RU_RUS,
Common::kPlatformWindows,
- GF_GUILANGSWITCH | ADGF_TESTING,
+ GF_GUILANGSWITCH | ADGF_NO_FLAGS,
GUIO0()
},
@@ -69,7 +69,7 @@ static const char * const directoryGlobs[] = {
class BbvsMetaEngine : public AdvancedMetaEngine {
public:
BbvsMetaEngine() : AdvancedMetaEngine(Bbvs::gameDescriptions, sizeof(ADGameDescription), bbvsGames) {
- _singleid = "bbvs";
+ _singleId = "bbvs";
_maxScanDepth = 3;
_directoryGlobs = directoryGlobs;
}
@@ -116,7 +116,6 @@ SaveStateList BbvsMetaEngine::listSaves(const char *target) const {
pattern += ".###";
Common::StringArray filenames;
filenames = saveFileMan->listSavefiles(pattern.c_str());
- Common::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
@@ -131,6 +130,8 @@ SaveStateList BbvsMetaEngine::listSaves(const char *target) const {
}
}
}
+ // Sort saves based on slot number.
+ Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator());
return saveList;
}
diff --git a/engines/bbvs/dialogs.cpp b/engines/bbvs/dialogs.cpp
index ef7f3c9320..c8470f8eef 100644
--- a/engines/bbvs/dialogs.cpp
+++ b/engines/bbvs/dialogs.cpp
@@ -102,7 +102,7 @@ void MainMenu::reflowLayout() {
_w = 2 * buttonWidth + buttonPadding;
_h = 3 * buttonHeight + 3 * buttonPadding;
_x = (screenW - _w) / 2;
- _y = screenH - _h;
+ _y = screenH - _h - 2;
int x = 0, y = 0;
diff --git a/engines/cge/POTFILES b/engines/cge/POTFILES
index 55430683c3..f8aef4932d 100644
--- a/engines/cge/POTFILES
+++ b/engines/cge/POTFILES
@@ -1,2 +1,3 @@
engines/cge/detection.cpp
+engines/cge/events.cpp
diff --git a/engines/cge/detection.cpp b/engines/cge/detection.cpp
index 47c5f56b6e..52168dc934 100644
--- a/engines/cge/detection.cpp
+++ b/engines/cge/detection.cpp
@@ -115,7 +115,7 @@ static const ADExtraGuiOptionsMap optionsList[] = {
class CGEMetaEngine : public AdvancedMetaEngine {
public:
CGEMetaEngine() : AdvancedMetaEngine(CGE::gameDescriptions, sizeof(ADGameDescription), CGEGames, optionsList) {
- _singleid = "soltys";
+ _singleId = "soltys";
}
virtual const char *getName() const {
@@ -199,7 +199,6 @@ SaveStateList CGEMetaEngine::listSaves(const char *target) const {
pattern += ".###";
filenames = saveFileMan->listSavefiles(pattern);
- sort(filenames.begin(), filenames.end()); // Sort (hopefully ensuring we are sorted numerically..)
SaveStateList saveList;
for (Common::StringArray::const_iterator filename = filenames.begin(); filename != filenames.end(); ++filename) {
@@ -235,6 +234,8 @@ SaveStateList CGEMetaEngine::listSaves(const char *target) const {
}
}
+ // Sort saves based on slot number.
+ Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator());
return saveList;
}
diff --git a/engines/cge/events.cpp b/engines/cge/events.cpp
index 24b3a270cf..c2f8982592 100644
--- a/engines/cge/events.cpp
+++ b/engines/cge/events.cpp
@@ -30,6 +30,7 @@
#include "gui/message.h"
#include "common/config-manager.h"
#include "common/events.h"
+#include "common/translation.h"
#include "engines/advancedDetector.h"
#include "cge/events.h"
#include "cge/events.h"
@@ -70,7 +71,7 @@ bool Keyboard::getKey(Common::Event &event) {
return false;
case Common::KEYCODE_F5:
if (_vm->canSaveGameStateCurrently()) {
- GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser("Save game:", "Save", true);
+ GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser(_("Save game:"), _("Save"), true);
int16 savegameId = dialog->runModalWithCurrentTarget();
Common::String savegameDescription = dialog->getResultString();
delete dialog;
@@ -81,7 +82,7 @@ bool Keyboard::getKey(Common::Event &event) {
return false;
case Common::KEYCODE_F7:
if (_vm->canLoadGameStateCurrently()) {
- GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser("Restore game:", "Restore", false);
+ GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser(_("Restore game:"), _("Restore"), false);
int16 savegameId = dialog->runModalWithCurrentTarget();
delete dialog;
diff --git a/engines/cge2/POTFILES b/engines/cge2/POTFILES
index 1e904763ec..3bff3258fd 100644
--- a/engines/cge2/POTFILES
+++ b/engines/cge2/POTFILES
@@ -1 +1,2 @@
engines/cge2/detection.cpp
+engines/cge2/events.cpp
diff --git a/engines/cge2/detection.cpp b/engines/cge2/detection.cpp
index a1867b0dfe..01119bb1fd 100644
--- a/engines/cge2/detection.cpp
+++ b/engines/cge2/detection.cpp
@@ -111,7 +111,7 @@ static const ADExtraGuiOptionsMap optionsList[] = {
class CGE2MetaEngine : public AdvancedMetaEngine {
public:
CGE2MetaEngine() : AdvancedMetaEngine(gameDescriptions, sizeof(ADGameDescription), CGE2Games, optionsList) {
- _singleid = "sfinx";
+ _singleId = "sfinx";
}
virtual const char *getName() const {
@@ -199,7 +199,6 @@ SaveStateList CGE2MetaEngine::listSaves(const char *target) const {
pattern += ".###";
filenames = saveFileMan->listSavefiles(pattern);
- sort(filenames.begin(), filenames.end()); // Sort (hopefully ensuring we are sorted numerically..)
SaveStateList saveList;
for (Common::StringArray::const_iterator filename = filenames.begin(); filename != filenames.end(); ++filename) {
@@ -235,6 +234,8 @@ SaveStateList CGE2MetaEngine::listSaves(const char *target) const {
}
}
+ // Sort saves based on slot number.
+ Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator());
return saveList;
}
diff --git a/engines/cge2/events.cpp b/engines/cge2/events.cpp
index 85743c8011..96cecc8e23 100644
--- a/engines/cge2/events.cpp
+++ b/engines/cge2/events.cpp
@@ -30,6 +30,7 @@
#include "gui/message.h"
#include "common/config-manager.h"
#include "common/events.h"
+#include "common/translation.h"
#include "engines/advancedDetector.h"
#include "cge2/events.h"
#include "cge2/text.h"
@@ -63,7 +64,7 @@ bool Keyboard::getKey(Common::Event &event) {
return false;
case Common::KEYCODE_F5:
if (_vm->canSaveGameStateCurrently()) {
- GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser("Save game:", "Save", true);
+ GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser(_("Save game:"), _("Save"), true);
int16 savegameId = dialog->runModalWithCurrentTarget();
Common::String savegameDescription = dialog->getResultString();
delete dialog;
@@ -74,7 +75,7 @@ bool Keyboard::getKey(Common::Event &event) {
return false;
case Common::KEYCODE_F7:
if (_vm->canLoadGameStateCurrently()) {
- GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser("Restore game:", "Restore", false);
+ GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser(_("Restore game:"), _("Restore"), false);
int16 savegameId = dialog->runModalWithCurrentTarget();
delete dialog;
diff --git a/engines/cge2/vga13h.cpp b/engines/cge2/vga13h.cpp
index eb111c3255..54f5c00d93 100644
--- a/engines/cge2/vga13h.cpp
+++ b/engines/cge2/vga13h.cpp
@@ -141,7 +141,7 @@ Sprite::Sprite(CGE2Engine *vm)
memset(_actionCtrl, 0, sizeof(_actionCtrl));
memset(_file, 0, sizeof(_file));
memset(&_flags, 0, sizeof(_flags));
- _flags._frnt = 1;
+ _flags._frnt = true;
}
Sprite::Sprite(CGE2Engine *vm, BitmapPtr shpP, int cnt)
@@ -152,7 +152,7 @@ Sprite::Sprite(CGE2Engine *vm, BitmapPtr shpP, int cnt)
memset(_actionCtrl, 0, sizeof(_actionCtrl));
memset(_file, 0, sizeof(_file));
memset(&_flags, 0, sizeof(_flags));
- _flags._frnt = 1;
+ _flags._frnt = true;
setShapeList(shpP, cnt);
}
diff --git a/engines/cine/anim.cpp b/engines/cine/anim.cpp
index c6099447d8..6ecf07fe15 100644
--- a/engines/cine/anim.cpp
+++ b/engines/cine/anim.cpp
@@ -535,7 +535,7 @@ int loadSpl(const char *resourceName, int16 idx) {
entry = idx < 0 ? emptyAnimSpace() : idx;
assert(entry >= 0);
- 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, ANIM_RAW, g_cine->_partBuffer[foundFileIdx].unpackedSize, 1, foundFileIdx, 0, currentPartName);
free(dataPtr);
return entry + 1;
diff --git a/engines/cine/detection.cpp b/engines/cine/detection.cpp
index dac7add16b..ec01e8734d 100644
--- a/engines/cine/detection.cpp
+++ b/engines/cine/detection.cpp
@@ -80,12 +80,12 @@ static const ADExtraGuiOptionsMap optionsList[] = {
class CineMetaEngine : public AdvancedMetaEngine {
public:
CineMetaEngine() : AdvancedMetaEngine(Cine::gameDescriptions, sizeof(Cine::CINEGameDescription), cineGames, optionsList) {
- _singleid = "cine";
- _guioptions = GUIO2(GUIO_NOSPEECH, GAMEOPTION_ORIGINAL_SAVELOAD);
+ _singleId = "cine";
+ _guiOptions = GUIO2(GUIO_NOSPEECH, GAMEOPTION_ORIGINAL_SAVELOAD);
}
- virtual GameDescriptor findGame(const char *gameid) const {
- return Engines::findGameID(gameid, _gameids, obsoleteGameIDsTable);
+ virtual GameDescriptor findGame(const char *gameId) const {
+ return Engines::findGameID(gameId, _gameIds, obsoleteGameIDsTable);
}
virtual const char *getName() const {
@@ -137,7 +137,6 @@ SaveStateList CineMetaEngine::listSaves(const char *target) const {
Common::String pattern = target;
pattern += ".#";
Common::StringArray filenames = saveFileMan->listSavefiles(pattern);
- sort(filenames.begin(), filenames.end());
Common::StringArray::const_iterator file;
Common::String filename = target;
@@ -169,6 +168,8 @@ SaveStateList CineMetaEngine::listSaves(const char *target) const {
delete in;
+ // Sort saves based on slot number.
+ Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator());
return saveList;
}
diff --git a/engines/cine/saveload.cpp b/engines/cine/saveload.cpp
index 1f4f286694..dfd3a1f4bc 100644
--- a/engines/cine/saveload.cpp
+++ b/engines/cine/saveload.cpp
@@ -691,6 +691,11 @@ bool CineEngine::loadPlainSaveFW(Common::SeekableReadStream &in, CineSaveGameFor
}
if (strlen(bgName)) {
+ if (g_cine->getGameType() == GType_FW && (g_cine->getFeatures() & GF_CD)) {
+ char buffer[20];
+ removeExtention(buffer, bgName);
+ g_sound->setBgMusic(atoi(buffer + 1));
+ }
loadBg(bgName);
}
diff --git a/engines/cine/script_fw.cpp b/engines/cine/script_fw.cpp
index 6ad38f4433..86eb709d5a 100644
--- a/engines/cine/script_fw.cpp
+++ b/engines/cine/script_fw.cpp
@@ -1858,7 +1858,9 @@ int FWScript::o1_playSample() {
if (g_cine->getGameType() == Cine::GType_OS && size == 0) {
return 0;
}
- g_sound->stopMusic();
+ // The DOS CD version of Future Wars uses CD audio for music
+ if (!(g_cine->getGameType() == Cine::GType_FW && (g_cine->getFeatures() & GF_CD)))
+ g_sound->stopMusic();
if (size == 0xFFFF) {
g_sound->playSound(channel, 0, data, 0, 0, 0, volume, 0);
} else {
diff --git a/engines/composer/configure.engine b/engines/composer/configure.engine
index 71a79acb5d..17120a3a3d 100644
--- a/engines/composer/configure.engine
+++ b/engines/composer/configure.engine
@@ -1,3 +1,3 @@
# This file is included from the main "configure" script
# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps]
-add_engine composer "Magic Composer" yes
+add_engine composer "Magic Composer" yes "" "" "highres"
diff --git a/engines/composer/detection.cpp b/engines/composer/detection.cpp
index 036c1e88d8..a3ab18ae54 100644
--- a/engines/composer/detection.cpp
+++ b/engines/composer/detection.cpp
@@ -38,7 +38,7 @@ int ComposerEngine::getGameType() const {
}
const char *ComposerEngine::getGameId() const {
- return _gameDescription->desc.gameid;
+ return _gameDescription->desc.gameId;
}
uint32 ComposerEngine::getFeatures() const {
@@ -81,12 +81,12 @@ static const ComposerGameDescription gameDescriptions[] = {
GType_ComposerV1
},
- // Magic Tales: Baba Yaga and the Magic Geese Mac - from bug #3466402
+ // Magic Tales: Baba Yaga and the Magic Geese Mac - from bug #3466402, #7025
{
{
"babayaga",
"",
- AD_ENTRY1("Baba Yaga", "ae3a4445f42fe10253da7ee4ea0d37"),
+ AD_ENTRY1s("Baba Yaga", "ae3a4445f42fe10253da7ee4ea0d37d6", 44321),
Common::EN_ANY,
Common::kPlatformMacintosh,
ADGF_NO_FLAGS,
@@ -386,7 +386,7 @@ static const char *directoryGlobs[] = {
class ComposerMetaEngine : public AdvancedMetaEngine {
public:
ComposerMetaEngine() : AdvancedMetaEngine(Composer::gameDescriptions, sizeof(Composer::ComposerGameDescription), composerGames) {
- _singleid = "composer";
+ _singleId = "composer";
_maxScanDepth = 2;
_directoryGlobs = directoryGlobs;
}
diff --git a/engines/cruise/detection.cpp b/engines/cruise/detection.cpp
index 4d0ed2bf91..6f5d236173 100644
--- a/engines/cruise/detection.cpp
+++ b/engines/cruise/detection.cpp
@@ -35,7 +35,7 @@ struct CRUISEGameDescription {
};
const char *CruiseEngine::getGameId() const {
- return _gameDescription->desc.gameid;
+ return _gameDescription->desc.gameId;
}
Common::Language CruiseEngine::getLanguage() const {
@@ -196,8 +196,8 @@ static const CRUISEGameDescription gameDescriptions[] = {
class CruiseMetaEngine : public AdvancedMetaEngine {
public:
CruiseMetaEngine() : AdvancedMetaEngine(Cruise::gameDescriptions, sizeof(Cruise::CRUISEGameDescription), cruiseGames) {
- _singleid = "cruise";
- _guioptions = GUIO2(GUIO_NOSPEECH, GUIO_NOMIDI);
+ _singleId = "cruise";
+ _guiOptions = GUIO2(GUIO_NOSPEECH, GUIO_NOMIDI);
}
virtual const char *getName() const {
@@ -231,7 +231,6 @@ SaveStateList CruiseMetaEngine::listSaves(const char *target) const {
Common::String pattern("cruise.s##");
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) {
@@ -250,6 +249,8 @@ SaveStateList CruiseMetaEngine::listSaves(const char *target) const {
}
}
+ // Sort saves based on slot number.
+ Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator());
return saveList;
}
diff --git a/engines/draci/detection.cpp b/engines/draci/detection.cpp
index a32dae3216..65427bd8cd 100644
--- a/engines/draci/detection.cpp
+++ b/engines/draci/detection.cpp
@@ -84,7 +84,7 @@ const ADGameDescription gameDescriptions[] = {
class DraciMetaEngine : public AdvancedMetaEngine {
public:
DraciMetaEngine() : AdvancedMetaEngine(Draci::gameDescriptions, sizeof(ADGameDescription), draciGames) {
- _singleid = "draci";
+ _singleId = "draci";
}
virtual const char *getName() const {
@@ -120,7 +120,6 @@ SaveStateList DraciMetaEngine::listSaves(const char *target) const {
Common::String pattern("draci.s##");
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) {
@@ -143,6 +142,8 @@ SaveStateList DraciMetaEngine::listSaves(const char *target) const {
}
}
+ // Sort saves based on slot number.
+ Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator());
return saveList;
}
diff --git a/engines/drascula/detection.cpp b/engines/drascula/detection.cpp
index fbf58faaa1..ffec393a0a 100644
--- a/engines/drascula/detection.cpp
+++ b/engines/drascula/detection.cpp
@@ -310,8 +310,8 @@ SaveStateDescriptor loadMetaData(Common::ReadStream *s, int slot, bool setPlayTi
class DrasculaMetaEngine : public AdvancedMetaEngine {
public:
DrasculaMetaEngine() : AdvancedMetaEngine(Drascula::gameDescriptions, sizeof(Drascula::DrasculaGameDescription), drasculaGames) {
- _singleid = "drascula";
- _guioptions = GUIO1(GUIO_NOMIDI);
+ _singleId = "drascula";
+ _guiOptions = GUIO1(GUIO_NOMIDI);
}
virtual const char *getName() const {
@@ -355,7 +355,6 @@ SaveStateList DrasculaMetaEngine::listSaves(const char *target) const {
pattern += ".###";
filenames = saveFileMan->listSavefiles(pattern);
- sort(filenames.begin(), filenames.end()); // Sort (hopefully ensuring we are sorted numerically..)
SaveStateList saveList;
int slotNum = 0;
@@ -378,6 +377,8 @@ SaveStateList DrasculaMetaEngine::listSaves(const char *target) const {
}
}
+ // Sort saves based on slot number.
+ Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator());
return saveList;
}
diff --git a/engines/dreamweb/detection.cpp b/engines/dreamweb/detection.cpp
index 4fc624c2f3..764171bcb0 100644
--- a/engines/dreamweb/detection.cpp
+++ b/engines/dreamweb/detection.cpp
@@ -70,8 +70,8 @@ public:
AdvancedMetaEngine(DreamWeb::gameDescriptions,
sizeof(DreamWeb::DreamWebGameDescription), dreamWebGames,
gameGuiOptions) {
- _singleid = "dreamweb";
- _guioptions = GUIO1(GUIO_NOMIDI);
+ _singleId = "dreamweb";
+ _guiOptions = GUIO1(GUIO_NOMIDI);
}
virtual const char *getName() const {
@@ -128,7 +128,6 @@ bool DreamWebMetaEngine::createInstance(OSystem *syst, Engine **engine, const AD
SaveStateList DreamWebMetaEngine::listSaves(const char *target) const {
Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
Common::StringArray files = saveFileMan->listSavefiles("DREAMWEB.D##");
- Common::sort(files.begin(), files.end());
SaveStateList saveList;
for (uint i = 0; i < files.size(); ++i) {
@@ -146,6 +145,8 @@ SaveStateList DreamWebMetaEngine::listSaves(const char *target) const {
saveList.push_back(sd);
}
+ // Sort saves based on slot number.
+ Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator());
return saveList;
}
diff --git a/engines/engine.cpp b/engines/engine.cpp
index 475cc77064..d3b9b113cf 100644
--- a/engines/engine.cpp
+++ b/engines/engine.cpp
@@ -292,7 +292,7 @@ void splashScreen() {
Common::Event event;
while (time0 + 600 > g_system->getMillis()) {
g_system->updateScreen();
- g_system->getEventManager()->pollEvent(event);
+ (void)g_system->getEventManager()->pollEvent(event);
g_system->delayMillis(10);
}
g_system->hideOverlay();
diff --git a/engines/fullpipe/configure.engine b/engines/fullpipe/configure.engine
index a9042449db..611d0188dc 100644
--- a/engines/fullpipe/configure.engine
+++ b/engines/fullpipe/configure.engine
@@ -1,3 +1,3 @@
# This file is included from the main "configure" script
# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps]
-add_engine fullpipe "Full Pipe" no "" "" "16bit"
+add_engine fullpipe "Full Pipe" no "" "" "16bit highres"
diff --git a/engines/fullpipe/detection.cpp b/engines/fullpipe/detection.cpp
index ccd55935e6..6f92f19f24 100644
--- a/engines/fullpipe/detection.cpp
+++ b/engines/fullpipe/detection.cpp
@@ -32,7 +32,7 @@
namespace Fullpipe {
const char *FullpipeEngine::getGameId() const {
- return _gameDescription->gameid;
+ return _gameDescription->gameId;
}
}
@@ -76,7 +76,7 @@ static const ADGameDescription gameDescriptions[] = {
class FullpipeMetaEngine : public AdvancedMetaEngine {
public:
FullpipeMetaEngine() : AdvancedMetaEngine(Fullpipe::gameDescriptions, sizeof(ADGameDescription), fullpipeGames) {
- _singleid = "fullpipe";
+ _singleId = "fullpipe";
}
virtual const char *getName() const {
@@ -111,7 +111,6 @@ SaveStateList FullpipeMetaEngine::listSaves(const char *target) const {
Common::String pattern("fullpipe.s##");
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) {
@@ -130,6 +129,8 @@ SaveStateList FullpipeMetaEngine::listSaves(const char *target) const {
}
}
+ // Sort saves based on slot number.
+ Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator());
return saveList;
}
diff --git a/engines/game.cpp b/engines/game.cpp
index 85ad6fe2e8..7ff51a99cc 100644
--- a/engines/game.cpp
+++ b/engines/game.cpp
@@ -26,8 +26,8 @@
const PlainGameDescriptor *findPlainGameDescriptor(const char *gameid, const PlainGameDescriptor *list) {
const PlainGameDescriptor *g = list;
- while (g->gameid) {
- if (0 == scumm_stricmp(gameid, g->gameid))
+ while (g->gameId) {
+ if (0 == scumm_stricmp(gameid, g->gameId))
return g;
g++;
}
@@ -40,7 +40,7 @@ GameDescriptor::GameDescriptor() {
}
GameDescriptor::GameDescriptor(const PlainGameDescriptor &pgd, Common::String guioptions) {
- setVal("gameid", pgd.gameid);
+ setVal("gameid", pgd.gameId);
setVal("description", pgd.description);
if (!guioptions.empty())
diff --git a/engines/game.h b/engines/game.h
index a9bec8f9e0..e01e5c6885 100644
--- a/engines/game.h
+++ b/engines/game.h
@@ -36,7 +36,7 @@
* consisting of PlainGameDescriptors.
*/
struct PlainGameDescriptor {
- const char *gameid;
+ const char *gameId;
const char *description;
};
@@ -108,7 +108,7 @@ public:
GameList() {}
GameList(const GameList &list) : Common::Array<GameDescriptor>(list) {}
GameList(const PlainGameDescriptor *g) {
- while (g->gameid) {
+ while (g->gameId) {
push_back(GameDescriptor(*g));
g++;
}
diff --git a/engines/gob/detection/detection.cpp b/engines/gob/detection/detection.cpp
index 3b26f63c39..b0aa78f416 100644
--- a/engines/gob/detection/detection.cpp
+++ b/engines/gob/detection/detection.cpp
@@ -33,7 +33,7 @@ class GobMetaEngine : public AdvancedMetaEngine {
public:
GobMetaEngine();
- virtual GameDescriptor findGame(const char *gameid) const;
+ virtual GameDescriptor findGame(const char *gameId) const;
virtual const ADGameDescription *fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const;
@@ -55,12 +55,12 @@ private:
GobMetaEngine::GobMetaEngine() :
AdvancedMetaEngine(Gob::gameDescriptions, sizeof(Gob::GOBGameDescription), gobGames) {
- _singleid = "gob";
- _guioptions = GUIO1(GUIO_NOLAUNCHLOAD);
+ _singleId = "gob";
+ _guiOptions = GUIO1(GUIO_NOLAUNCHLOAD);
}
-GameDescriptor GobMetaEngine::findGame(const char *gameid) const {
- return Engines::findGameID(gameid, _gameids, obsoleteGameIDsTable);
+GameDescriptor GobMetaEngine::findGame(const char *gameId) const {
+ return Engines::findGameID(gameId, _gameIds, obsoleteGameIDsTable);
}
const ADGameDescription *GobMetaEngine::fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const {
diff --git a/engines/gob/script.cpp b/engines/gob/script.cpp
index d6e9841e0a..9298f159a4 100644
--- a/engines/gob/script.cpp
+++ b/engines/gob/script.cpp
@@ -215,7 +215,7 @@ char *Script::readString(int32 length) {
}
byte Script::peekByte(int32 offset) {
- byte v;
+ byte v = 0;
peek(&v, 1, offset);
diff --git a/engines/gob/sound/adlib.cpp b/engines/gob/sound/adlib.cpp
index 1e024d5a50..2a9a716058 100644
--- a/engines/gob/sound/adlib.cpp
+++ b/engines/gob/sound/adlib.cpp
@@ -283,7 +283,12 @@ void AdLib::initOPL() {
_voiceOn [i] = 0;
}
- _opl->reset();
+ /* NOTE: We used to completely reset the OPL here, via _opl->reset(). However,
+ * with the OPL timer change in 73e8ac2a, reset() must not be called while
+ * the callback is still active. With the Gob AdLib rewrite in 03ef6689,
+ * this reset shouldn't be necessary anymore either, since this function
+ * here cleans everything properly anyway. If suddenly a certain piece of
+ * music in a Gob game sounds weird, we need to re-examine that. */
initOperatorVolumes();
resetFreqs();
diff --git a/engines/groovie/configure.engine b/engines/groovie/configure.engine
index 212a49bec8..f283731a58 100644
--- a/engines/groovie/configure.engine
+++ b/engines/groovie/configure.engine
@@ -1,4 +1,4 @@
# This file is included from the main "configure" script
# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps]
-add_engine groovie "Groovie" yes "groovie2" "7th Guest"
+add_engine groovie "Groovie" yes "groovie2" "7th Guest" "highres"
add_engine groovie2 "Groovie 2 games" no "" "" "jpeg 16bit"
diff --git a/engines/groovie/detection.cpp b/engines/groovie/detection.cpp
index a249b66858..b12e264a57 100644
--- a/engines/groovie/detection.cpp
+++ b/engines/groovie/detection.cpp
@@ -322,7 +322,7 @@ static const ADExtraGuiOptionsMap optionsList[] = {
class GroovieMetaEngine : public AdvancedMetaEngine {
public:
GroovieMetaEngine() : AdvancedMetaEngine(gameDescriptions, sizeof(GroovieGameDescription), groovieGames, optionsList) {
- _singleid = "groovie";
+ _singleId = "groovie";
// Use kADFlagUseExtraAsHint in order to distinguish the 11th hour from
// its "Making of" as well as the Clandestiny Trailer; they all share
@@ -333,7 +333,7 @@ public:
// to the detection entries. In the latter case, this TODO should be
// replaced with an according explanation.
_flags = kADFlagUseExtraAsHint;
- _guioptions = GUIO3(GUIO_NOSUBTITLES, GUIO_NOSFX, GUIO_NOASPECT);
+ _guiOptions = GUIO3(GUIO_NOSUBTITLES, GUIO_NOSFX, GUIO_NOASPECT);
// Need MIDI directory to detect 11H Mac Installed
_maxScanDepth = 2;
diff --git a/engines/groovie/groovie.cpp b/engines/groovie/groovie.cpp
index b42cf09245..2021cef6e8 100644
--- a/engines/groovie/groovie.cpp
+++ b/engines/groovie/groovie.cpp
@@ -69,7 +69,7 @@ GroovieEngine::GroovieEngine(OSystem *syst, const GroovieGameDescription *gd) :
// Initialize the custom debug levels
DebugMan.addDebugChannel(kDebugVideo, "Video", "Debug video and audio playback");
- DebugMan.addDebugChannel(kDebugResource, "Resource", "Debug resouce management");
+ DebugMan.addDebugChannel(kDebugResource, "Resource", "Debug resource management");
DebugMan.addDebugChannel(kDebugScript, "Script", "Debug the scripts");
DebugMan.addDebugChannel(kDebugUnknown, "Unknown", "Report values of unknown data in files");
DebugMan.addDebugChannel(kDebugHotspots, "Hotspots", "Show the hotspots");
diff --git a/engines/hopkins/configure.engine b/engines/hopkins/configure.engine
index c38ecd4cd2..cd9f50a5f9 100644
--- a/engines/hopkins/configure.engine
+++ b/engines/hopkins/configure.engine
@@ -1,3 +1,3 @@
# This file is included from the main "configure" script
# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps]
-add_engine hopkins "Hopkins FBI" yes "" "" "16bit"
+add_engine hopkins "Hopkins FBI" yes "" "" "16bit highres"
diff --git a/engines/hopkins/detection.cpp b/engines/hopkins/detection.cpp
index 670efb8898..cc1e84f5f8 100644
--- a/engines/hopkins/detection.cpp
+++ b/engines/hopkins/detection.cpp
@@ -153,7 +153,6 @@ SaveStateList HopkinsMetaEngine::listSaves(const char *target) const {
Common::String pattern = Common::String::format("%s.0##", target);
filenames = saveFileMan->listSavefiles(pattern);
- sort(filenames.begin(), filenames.end()); // Sort to get the files in numerical order
Hopkins::hopkinsSavegameHeader header;
@@ -178,6 +177,8 @@ SaveStateList HopkinsMetaEngine::listSaves(const char *target) const {
}
}
+ // Sort saves based on slot number.
+ Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator());
return saveList;
}
diff --git a/engines/hugo/POTFILES b/engines/hugo/POTFILES
new file mode 100644
index 0000000000..ff61e12ca5
--- /dev/null
+++ b/engines/hugo/POTFILES
@@ -0,0 +1 @@
+engines/hugo/file.cpp
diff --git a/engines/hugo/detection.cpp b/engines/hugo/detection.cpp
index a005e649d4..4e4746c002 100644
--- a/engines/hugo/detection.cpp
+++ b/engines/hugo/detection.cpp
@@ -41,7 +41,7 @@ uint32 HugoEngine::getFeatures() const {
}
const char *HugoEngine::getGameId() const {
- return _gameDescription->desc.gameid;
+ return _gameDescription->desc.gameId;
}
@@ -180,7 +180,6 @@ SaveStateList HugoMetaEngine::listSaves(const char *target) const {
pattern += "-##.SAV";
filenames = saveFileMan->listSavefiles(pattern);
- sort(filenames.begin(), filenames.end()); // Sort (hopefully ensuring we are sorted numerically..)
SaveStateList saveList;
char slot[3];
@@ -217,6 +216,8 @@ SaveStateList HugoMetaEngine::listSaves(const char *target) const {
}
}
+ // Sort saves based on slot number.
+ Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator());
return saveList;
}
diff --git a/engines/hugo/file.cpp b/engines/hugo/file.cpp
index e2633977a8..7a3538ea63 100644
--- a/engines/hugo/file.cpp
+++ b/engines/hugo/file.cpp
@@ -32,6 +32,7 @@
#include "common/savefile.h"
#include "common/textconsole.h"
#include "common/config-manager.h"
+#include "common/translation.h"
#include "graphics/surface.h"
#include "graphics/thumbnail.h"
@@ -294,7 +295,7 @@ bool FileManager::saveGame(const int16 slot, const Common::String &descrip) {
Common::String savegameDescription;
if (slot == -1) {
- GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser("Save game:", "Save", true);
+ GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser(_("Save game:"), _("Save"), true);
savegameId = dialog->runModalWithCurrentTarget();
savegameDescription = dialog->getResultString();
delete dialog;
@@ -396,7 +397,7 @@ bool FileManager::restoreGame(const int16 slot) {
int16 savegameId;
if (slot == -1) {
- GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser("Restore game:", "Restore", false);
+ GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser(_("Restore game:"), _("Restore"), false);
savegameId = dialog->runModalWithCurrentTarget();
delete dialog;
} else {
diff --git a/engines/kyra/debugger.cpp b/engines/kyra/debugger.cpp
index 4a90722a35..6683f973ca 100644
--- a/engines/kyra/debugger.cpp
+++ b/engines/kyra/debugger.cpp
@@ -542,7 +542,7 @@ bool Debugger_EoB::cmdSaveOriginal(int argc, const char **argv) {
debugPrintf("Failure.\n");
}
} else {
- debugPrintf("Syntax: save_original\n (Saves game in original file format to a file which can be used with the orginal game executable.)\n\n");
+ debugPrintf("Syntax: save_original\n (Saves game in original file format to a file which can be used with the original game executable.)\n\n");
}
return true;
@@ -562,7 +562,7 @@ bool Debugger_EoB::cmdSaveOriginal(int argc, const char **argv) {
return true;
}
- debugPrintf("Syntax: save_original <slot>\n (Saves game in original file format to a file which can be used with the orginal game executable.\n A save slot between 0 and 5 must be specified.)\n\n");
+ debugPrintf("Syntax: save_original <slot>\n (Saves game in original file format to a file which can be used with the original game executable.\n A save slot between 0 and 5 must be specified.)\n\n");
return true;
}
diff --git a/engines/kyra/detection.cpp b/engines/kyra/detection.cpp
index a4786ef583..989a45b420 100644
--- a/engines/kyra/detection.cpp
+++ b/engines/kyra/detection.cpp
@@ -246,7 +246,6 @@ SaveStateList KyraMetaEngine::listSaves(const char *target) const {
Common::StringArray filenames;
filenames = saveFileMan->listSavefiles(pattern);
- Common::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) {
@@ -269,6 +268,8 @@ SaveStateList KyraMetaEngine::listSaves(const char *target) const {
}
}
+ // Sort saves based on slot number.
+ Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator());
return saveList;
}
diff --git a/engines/kyra/detection_tables.h b/engines/kyra/detection_tables.h
index 2ee0262ef2..773d491423 100644
--- a/engines/kyra/detection_tables.h
+++ b/engines/kyra/detection_tables.h
@@ -1546,7 +1546,7 @@ const KYRAGameDescription adGameDescs[] = {
},
Common::EN_ANY,
Common::kPlatformDOS,
- ADGF_TESTING,
+ ADGF_NO_FLAGS,
GUIO7(GUIO_NOSPEECH, GUIO_MIDIADLIB, GUIO_MIDIPCSPK, GUIO_RENDERVGA, GUIO_RENDEREGA, GUIO_RENDERCGA, GAMEOPTION_EOB_HPGRAPHS)
},
EOB_FLAGS
@@ -1562,7 +1562,7 @@ const KYRAGameDescription adGameDescs[] = {
},
Common::DE_DEU,
Common::kPlatformDOS,
- ADGF_TESTING,
+ ADGF_NO_FLAGS,
GUIO7(GUIO_NOSPEECH, GUIO_MIDIADLIB, GUIO_MIDIPCSPK, GUIO_RENDERVGA, GUIO_RENDEREGA, GUIO_RENDERCGA, GAMEOPTION_EOB_HPGRAPHS)
},
EOB_FLAGS
@@ -1576,10 +1576,10 @@ const KYRAGameDescription adGameDescs[] = {
{ "EOBDATA3.PAK", 0, "3ed915ab5b94d60dbfe1b55379889c51", -1 },
{ 0, 0, 0, 0 }
},
- Common::IT_ITA,
- Common::kPlatformDOS,
- ADGF_TESTING,
- GUIO7(GUIO_NOSPEECH, GUIO_MIDIADLIB, GUIO_MIDIPCSPK, GUIO_RENDERVGA, GUIO_RENDEREGA, GUIO_RENDERCGA, GAMEOPTION_EOB_HPGRAPHS)
+ Common::IT_ITA,
+ Common::kPlatformDOS,
+ ADGF_NO_FLAGS,
+ GUIO7(GUIO_NOSPEECH, GUIO_MIDIADLIB, GUIO_MIDIPCSPK, GUIO_RENDERVGA, GUIO_RENDEREGA, GUIO_RENDERCGA, GAMEOPTION_EOB_HPGRAPHS)
},
EOB_FLAGS
},
@@ -1594,7 +1594,7 @@ const KYRAGameDescription adGameDescs[] = {
},
Common::EN_ANY,
Common::kPlatformDOS,
- ADGF_TESTING,
+ ADGF_NO_FLAGS,
GUIO6(GUIO_NOSPEECH, GUIO_MIDIADLIB, GUIO_MIDIPCSPK, GUIO_RENDERVGA, GUIO_RENDEREGA, GAMEOPTION_EOB_HPGRAPHS)
},
EOB2_FLAGS
@@ -1610,7 +1610,7 @@ const KYRAGameDescription adGameDescs[] = {
},
Common::DE_DEU,
Common::kPlatformDOS,
- ADGF_TESTING,
+ ADGF_NO_FLAGS,
GUIO6(GUIO_NOSPEECH, GUIO_MIDIADLIB, GUIO_MIDIPCSPK, GUIO_RENDERVGA, GUIO_RENDEREGA, GAMEOPTION_EOB_HPGRAPHS)
},
EOB2_FLAGS
diff --git a/engines/kyra/gui_lol.cpp b/engines/kyra/gui_lol.cpp
index cfdf762bba..5ff0b6a109 100644
--- a/engines/kyra/gui_lol.cpp
+++ b/engines/kyra/gui_lol.cpp
@@ -2219,7 +2219,7 @@ int GUI_LoL::runMenu(Menu &menu) {
int fW = (d->w << 3) - wW;
int fC = 0;
- // LoL doesnt't have default higlighted items. No item should be
+ // LoL doesn't have default higlighted items. No item should be
// highlighted when entering a new menu.
// Instead, the respevtive struct entry is used to determine whether
// a menu has scroll buttons or slider bars.
diff --git a/engines/kyra/gui_v1.cpp b/engines/kyra/gui_v1.cpp
index a75c14b071..92891f71b2 100644
--- a/engines/kyra/gui_v1.cpp
+++ b/engines/kyra/gui_v1.cpp
@@ -200,7 +200,7 @@ void GUI_v1::processHighlights(Menu &menu) {
int mouseY = p.y;
if (_vm->game() == GI_LOL && menu.highlightedItem != 255) {
- // LoL doesnt't have default highlighted items.
+ // LoL doesn't have default highlighted items.
// We use a highlightedItem value of 255 for this.
// With LoL no highlighting should take place unless the
diff --git a/engines/kyra/sound.cpp b/engines/kyra/sound.cpp
index 3c3aff6ea9..6c0d529f96 100644
--- a/engines/kyra/sound.cpp
+++ b/engines/kyra/sound.cpp
@@ -37,7 +37,7 @@ namespace Kyra {
Sound::Sound(KyraEngine_v1 *vm, Audio::Mixer *mixer)
: _vm(vm), _mixer(mixer), _soundChannels(), _musicEnabled(1),
- _sfxEnabled(true) {
+ _sfxEnabled(true) {
}
Sound::~Sound() {
@@ -165,7 +165,7 @@ bool Sound::allVoiceChannelsPlaying() const {
#pragma mark -
MixedSoundDriver::MixedSoundDriver(KyraEngine_v1 *vm, Audio::Mixer *mixer, Sound *music, Sound *sfx)
- : Sound(vm, mixer), _music(music), _sfx(sfx) {
+ : Sound(vm, mixer), _music(music), _sfx(sfx) {
}
MixedSoundDriver::~MixedSoundDriver() {
@@ -289,16 +289,16 @@ void KyraEngine_v1::snd_playWanderScoreViaMap(int command, int restart) {
//}
if (_flags.platform == Common::kPlatformDOS || _flags.platform == Common::kPlatformMacintosh) {
- assert(command*2+1 < _trackMapSize);
- if (_curMusicTheme != _trackMap[command*2]) {
- if (_trackMap[command*2] != -1 && _trackMap[command*2] != -2)
- snd_playTheme(_trackMap[command*2], -1);
+ assert(command * 2 + 1 < _trackMapSize);
+ if (_curMusicTheme != _trackMap[command * 2]) {
+ if (_trackMap[command * 2] != -1 && _trackMap[command * 2] != -2)
+ snd_playTheme(_trackMap[command * 2], -1);
}
if (command != 1) {
if (_lastMusicCommand != command) {
_sound->haltTrack();
- _sound->playTrack(_trackMap[command*2+1]);
+ _sound->playTrack(_trackMap[command * 2 + 1]);
}
} else {
_sound->beginFadeOut();
@@ -307,8 +307,8 @@ void KyraEngine_v1::snd_playWanderScoreViaMap(int command, int restart) {
if (command == -1) {
_sound->haltTrack();
} else {
- assert(command*2+1 < _trackMapSize);
- if (_trackMap[command*2] != -2 && command != _lastMusicCommand) {
+ assert(command * 2 + 1 < _trackMapSize);
+ if (_trackMap[command * 2] != -2 && command != _lastMusicCommand) {
_sound->haltTrack();
_sound->playTrack(command);
}
diff --git a/engines/lab/configure.engine b/engines/lab/configure.engine
index 18091b1b84..3be9545aa6 100644
--- a/engines/lab/configure.engine
+++ b/engines/lab/configure.engine
@@ -1,3 +1,3 @@
# This file is included from the main "configure" script
# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps]
-add_engine lab "Labyrinth of Time" no
+add_engine lab "Labyrinth of Time" yes
diff --git a/engines/lab/detection.cpp b/engines/lab/detection.cpp
index d01dff2843..1fd3ca8944 100644
--- a/engines/lab/detection.cpp
+++ b/engines/lab/detection.cpp
@@ -48,7 +48,7 @@ static const ADGameDescription labDescriptions[] = {
},
Common::EN_ANY,
Common::kPlatformDOS,
- ADGF_TESTING,
+ ADGF_NO_FLAGS,
GUIO0()
},
{
@@ -61,7 +61,7 @@ static const ADGameDescription labDescriptions[] = {
},
Common::EN_ANY,
Common::kPlatformDOS,
- Lab::GF_LOWRES | ADGF_TESTING,
+ Lab::GF_LOWRES | ADGF_NO_FLAGS,
GUIO0()
},
{
@@ -75,7 +75,7 @@ static const ADGameDescription labDescriptions[] = {
},
Common::EN_ANY,
Common::kPlatformWindows,
- ADGF_TESTING,
+ ADGF_NO_FLAGS,
GUIO0()
},
{
@@ -115,7 +115,7 @@ uint32 LabEngine::getFeatures() const {
class LabMetaEngine : public AdvancedMetaEngine {
public:
LabMetaEngine() : AdvancedMetaEngine(labDescriptions, sizeof(ADGameDescription), lab_setting) {
- _singleid = "lab";
+ _singleId = "lab";
_maxScanDepth = 4;
_directoryGlobs = directoryGlobs;
@@ -169,7 +169,6 @@ SaveStateList LabMetaEngine::listSaves(const char *target) const {
Common::StringArray filenames;
filenames = saveFileMan->listSavefiles(pattern.c_str());
- Common::sort(filenames.begin(), filenames.end()); // Sort (hopefully ensuring we are sorted numerically..)*/
SaveStateList saveList;
@@ -187,6 +186,8 @@ SaveStateList LabMetaEngine::listSaves(const char *target) const {
}
}
+ // Sort saves based on slot number.
+ Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator());
return saveList;
}
@@ -196,26 +197,7 @@ int LabMetaEngine::getMaximumSaveSlot() const {
void LabMetaEngine::removeSaveState(const char *target, int slot) const {
Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
- Common::String filename = Common::String::format("%s.%03u", target, slot);
-
- saveFileMan->removeSavefile(filename.c_str());
-
- Common::StringArray filenames;
- Common::String pattern = target;
- pattern += ".###";
- filenames = saveFileMan->listSavefiles(pattern.c_str());
- Common::sort(filenames.begin(), filenames.end()); // Sort (hopefully ensuring we are sorted numerically..)
-
- 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);
-
- // Rename every slot greater than the deleted slot,
- if (slotNum > slot) {
- saveFileMan->renameSavefile(file->c_str(), filename.c_str());
- filename = Common::String::format("%s.%03u", target, ++slot);
- }
- }
+ saveFileMan->removeSavefile(Common::String::format("%s.%03u", target, slot));
}
SaveStateDescriptor LabMetaEngine::querySaveMetaInfos(const char *target, int slot) const {
diff --git a/engines/lab/lab.cpp b/engines/lab/lab.cpp
index 0a4cf4ec50..9b0ebfc4e5 100644
--- a/engines/lab/lab.cpp
+++ b/engines/lab/lab.cpp
@@ -214,12 +214,6 @@ void LabEngine::updateEvents() {
Common::Error LabEngine::loadGameState(int slot) {
bool result = loadGame(slot);
- _curFileName = " ";
- _closeDataPtr = nullptr;
- _mainDisplay = true;
- _followingCrumbs = false;
- _event->simulateEvent();
- _graphics->_longWinInFront = false;
return (result) ? Common::kNoError : Common::kUserCanceled;
}
@@ -229,11 +223,11 @@ Common::Error LabEngine::saveGameState(int slot, const Common::String &desc) {
}
bool LabEngine::canLoadGameStateCurrently() {
- return !_anim->isPlaying() && !_introPlaying;
+ return !_introPlaying;
}
bool LabEngine::canSaveGameStateCurrently() {
- return !_anim->isPlaying() && !_introPlaying;
+ return !_introPlaying;
}
} // End of namespace Lab
diff --git a/engines/lab/processroom.cpp b/engines/lab/processroom.cpp
index 44c8d65d7c..5093e8ef85 100644
--- a/engines/lab/processroom.cpp
+++ b/engines/lab/processroom.cpp
@@ -238,6 +238,8 @@ void LabEngine::doActions(const ActionList &actionList) {
ActionList::const_iterator action;
for (action = actionList.begin(); action != actionList.end(); ++action) {
updateEvents();
+ if (_quitLab || shouldQuit())
+ return;
switch (action->_actionType) {
case kActionPlaySound:
@@ -381,6 +383,8 @@ void LabEngine::doActions(const ActionList &actionList) {
while (_system->getMillis() < targetMillis) {
updateEvents();
+ if (_quitLab || shouldQuit())
+ return;
_anim->diffNextFrame();
}
}
@@ -409,6 +413,8 @@ void LabEngine::doActions(const ActionList &actionList) {
case kActionWaitSound: // used in scene 44 (heart of the labyrinth / ending)
while (_music->isSoundEffectActive()) {
updateEvents();
+ if (_quitLab || shouldQuit())
+ return;
_anim->diffNextFrame();
waitTOF();
}
diff --git a/engines/lab/savegame.cpp b/engines/lab/savegame.cpp
index 16c4044839..656595e3e5 100644
--- a/engines/lab/savegame.cpp
+++ b/engines/lab/savegame.cpp
@@ -157,6 +157,11 @@ bool LabEngine::saveGame(int slot, const Common::String desc) {
file->finalize();
delete file;
+ _mainDisplay = true;
+ _alternate = false;
+ _event->simulateEvent();
+ _graphics->screenUpdate();
+
return true;
}
@@ -202,6 +207,17 @@ bool LabEngine::loadGame(int slot) {
delete file;
+ _curFileName = " ";
+ _closeDataPtr = nullptr;
+ _followingCrumbs = false;
+ _graphics->_longWinInFront = false;
+ _event->initMouse();
+
+ _mainDisplay = true;
+ _alternate = false;
+ _event->simulateEvent();
+ _graphics->screenUpdate();
+
return true;
}
@@ -238,11 +254,6 @@ bool LabEngine::saveRestoreGame() {
delete dialog;
}
- _alternate = false;
- _mainDisplay = true;
- _event->initMouse();
- _graphics->screenUpdate();
-
return isOK;
}
diff --git a/engines/lastexpress/configure.engine b/engines/lastexpress/configure.engine
index 807b1a088b..66bac55dea 100644
--- a/engines/lastexpress/configure.engine
+++ b/engines/lastexpress/configure.engine
@@ -1,3 +1,3 @@
# This file is included from the main "configure" script
# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps]
-add_engine lastexpress "The Last Express" no "" "" "16bit"
+add_engine lastexpress "The Last Express" no "" "" "16bit highres"
diff --git a/engines/lastexpress/detection.cpp b/engines/lastexpress/detection.cpp
index d790582104..52224e4ea5 100644
--- a/engines/lastexpress/detection.cpp
+++ b/engines/lastexpress/detection.cpp
@@ -199,8 +199,8 @@ static const ADGameDescription gameDescriptions[] = {
class LastExpressMetaEngine : public AdvancedMetaEngine {
public:
LastExpressMetaEngine() : AdvancedMetaEngine(gameDescriptions, sizeof(ADGameDescription), lastExpressGames) {
- _singleid = "lastexpress";
- _guioptions = GUIO2(GUIO_NOSUBTITLES, GUIO_NOSFX);
+ _singleId = "lastexpress";
+ _guiOptions = GUIO2(GUIO_NOSUBTITLES, GUIO_NOSFX);
}
const char *getName() const {
diff --git a/engines/lure/detection.cpp b/engines/lure/detection.cpp
index 902e8afd65..690a358bc3 100644
--- a/engines/lure/detection.cpp
+++ b/engines/lure/detection.cpp
@@ -193,12 +193,12 @@ class LureMetaEngine : public AdvancedMetaEngine {
public:
LureMetaEngine() : AdvancedMetaEngine(Lure::gameDescriptions, sizeof(Lure::LureGameDescription), lureGames) {
_md5Bytes = 1024;
- _singleid = "lure";
+ _singleId = "lure";
// Use kADFlagUseExtraAsHint to distinguish between EGA and VGA versions
// of italian Lure when their datafiles sit in the same directory.
_flags = kADFlagUseExtraAsHint;
- _guioptions = GUIO1(GUIO_NOSPEECH);
+ _guiOptions = GUIO1(GUIO_NOSPEECH);
}
virtual const char *getName() const {
@@ -245,7 +245,6 @@ SaveStateList LureMetaEngine::listSaves(const char *target) const {
Common::String pattern = "lure.###";
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) {
@@ -262,6 +261,8 @@ SaveStateList LureMetaEngine::listSaves(const char *target) const {
}
}
+ // Sort saves based on slot number.
+ Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator());
return saveList;
}
diff --git a/engines/made/detection.cpp b/engines/made/detection.cpp
index 7adc3e1f98..636c2d147c 100644
--- a/engines/made/detection.cpp
+++ b/engines/made/detection.cpp
@@ -521,7 +521,7 @@ static MadeGameDescription g_fallbackDesc = {
class MadeMetaEngine : public AdvancedMetaEngine {
public:
MadeMetaEngine() : AdvancedMetaEngine(Made::gameDescriptions, sizeof(Made::MadeGameDescription), madeGames) {
- _singleid = "made";
+ _singleId = "made";
}
virtual const char *getName() const {
diff --git a/engines/mads/debugger.cpp b/engines/mads/debugger.cpp
index 740c19abad..b9731b1d31 100644
--- a/engines/mads/debugger.cpp
+++ b/engines/mads/debugger.cpp
@@ -158,7 +158,7 @@ bool Debugger::Cmd_ShowCodes(int argc, const char **argv) {
Scene &scene = _vm->_game->_scene;
// Copy the depth/walk surface to the background and flag for screen refresh
- scene._depthSurface.copyTo(&scene._backgroundSurface);
+ scene._depthSurface.blitFrom(scene._backgroundSurface);
scene._spriteSlots.fullRefresh();
// Draw the locations of scene nodes onto the background
diff --git a/engines/mads/detection.cpp b/engines/mads/detection.cpp
index 0357bd4bcc..b3ba60b6d0 100644
--- a/engines/mads/detection.cpp
+++ b/engines/mads/detection.cpp
@@ -192,7 +192,6 @@ SaveStateList MADSMetaEngine::listSaves(const char *target) const {
MADS::MADSSavegameHeader header;
filenames = saveFileMan->listSavefiles(pattern);
- sort(filenames.begin(), filenames.end()); // Sort to get the files in numerical order
SaveStateList saveList;
for (Common::StringArray::const_iterator file = filenames.begin(); file != filenames.end(); ++file) {
@@ -213,6 +212,8 @@ SaveStateList MADSMetaEngine::listSaves(const char *target) const {
}
}
+ // Sort saves based on slot number.
+ Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator());
return saveList;
}
diff --git a/engines/mads/dialogs.cpp b/engines/mads/dialogs.cpp
index d9a1e53964..fa656a90c1 100644
--- a/engines/mads/dialogs.cpp
+++ b/engines/mads/dialogs.cpp
@@ -54,21 +54,19 @@ Dialog::~Dialog() {
void Dialog::save() {
_savedSurface = new MSurface(_width, _height);
- _vm->_screen.copyTo(_savedSurface,
+ _savedSurface->blitFrom(*_vm->_screen,
Common::Rect(_position.x, _position.y, _position.x + _width, _position.y + _height),
Common::Point());
- _vm->_screen.copyRectToScreen(getBounds());
+// _vm->_screen->copyRectToScreen(getBounds());
}
void Dialog::restore() {
if (_savedSurface) {
- _savedSurface->copyTo(&_vm->_screen, _position);
+ _vm->_screen->blitFrom(*_savedSurface, _position);
delete _savedSurface;
_savedSurface = nullptr;
- _vm->_screen.copyRectToScreen(getBounds());
-
Common::copy(&_dialogPalette[0], &_dialogPalette[8 * 3],
&_vm->_palette->_mainPalette[248 * 3]);
_vm->_palette->setPalette(&_vm->_palette->_mainPalette[248 * 3], 248, 8);
@@ -87,16 +85,16 @@ void Dialog::draw() {
// Draw the dialog
// Fill entire content of dialog
Common::Rect bounds = getBounds();
- _vm->_screen.fillRect(bounds, TEXTDIALOG_BACKGROUND);
+ _vm->_screen->fillRect(bounds, TEXTDIALOG_BACKGROUND);
// Draw the outer edge lines
- _vm->_screen.hLine(_position.x + 1, _position.y + _height - 2,
+ _vm->_screen->hLine(_position.x + 1, _position.y + _height - 2,
_position.x + _width - 2, TEXTDIALOG_EDGE);
- _vm->_screen.hLine(_position.x, _position.y + _height - 1,
+ _vm->_screen->hLine(_position.x, _position.y + _height - 1,
_position.x + _width - 1, TEXTDIALOG_EDGE);
- _vm->_screen.vLine(_position.x + _width - 2, _position.y + 2,
+ _vm->_screen->vLine(_position.x + _width - 2, _position.y + 2,
_position.y + _height - 2, TEXTDIALOG_EDGE);
- _vm->_screen.vLine(_position.x + _width - 1, _position.y + 1,
+ _vm->_screen->vLine(_position.x + _width - 1, _position.y + 1,
_position.y + _height - 1, TEXTDIALOG_EDGE);
// Draw the gravelly dialog content
@@ -125,8 +123,9 @@ void Dialog::calculateBounds() {
void Dialog::drawContent(const Common::Rect &r, int seed, byte color1, byte color2) {
uint16 currSeed = seed ? seed : 0xB78E;
+ Graphics::Surface dest = _vm->_screen->getSubArea(r);
for (int yp = 0; yp < r.height(); ++yp) {
- byte *destP = _vm->_screen.getBasePtr(r.left, r.top + yp);
+ byte *destP = (byte *)dest.getBasePtr(0, yp);
for (int xp = 0; xp < r.width(); ++xp) {
uint16 seedAdjust = currSeed;
@@ -326,7 +325,7 @@ void TextDialog::draw() {
for (int lineNum = 0; lineNum <= _numLines; ++lineNum) {
if (_lineXp[lineNum] == -1) {
// Draw a line across the entire dialog
- _vm->_screen.hLine(_position.x + 2,
+ _vm->_screen->hLine(_position.x + 2,
lineYp + (_font->getHeight() + 1) / 2,
_position.x + _width - 4, TEXTDIALOG_BLACK);
} else {
@@ -336,21 +335,19 @@ void TextDialog::draw() {
if (_lineXp[lineNum] & 0x40)
++yp;
- _font->writeString(&_vm->_screen, _lines[lineNum],
+ _font->writeString(_vm->_screen, _lines[lineNum],
Common::Point(xp, yp), 1);
if (_lineXp[lineNum] & 0x80) {
// Draw an underline under the text
int lineWidth = _font->getWidth(_lines[lineNum], 1);
- _vm->_screen.hLine(xp, yp + _font->getHeight(), xp + lineWidth,
+ _vm->_screen->hLine(xp, yp + _font->getHeight(), xp + lineWidth,
TEXTDIALOG_BLACK);
}
}
lineYp += _font->getHeight() + 1;
}
-
- _vm->_screen.copyRectToScreen(getBounds());
}
void TextDialog::calculateBounds() {
@@ -360,10 +357,10 @@ void TextDialog::calculateBounds() {
if (_position.y == -1)
_position.y = 100 - (_height / 2);
- if ((_position.x + _width) > _vm->_screen.getWidth())
- _position.x = _vm->_screen.getWidth() - (_position.x + _width);
- if ((_position.y + _height) > _vm->_screen.getHeight())
- _position.y = _vm->_screen.getHeight() - (_position.y + _height);
+ if ((_position.x + _width) > _vm->_screen->w)
+ _position.x = _vm->_screen->w - (_position.x + _width);
+ if ((_position.y + _height) > _vm->_screen->h)
+ _position.y = _vm->_screen->h - (_position.y + _height);
}
void TextDialog::drawWithInput() {
@@ -452,7 +449,7 @@ FullScreenDialog::FullScreenDialog(MADSEngine *vm) : _vm(vm) {
}
FullScreenDialog::~FullScreenDialog() {
- _vm->_screen.resetClipBounds();
+ _vm->_screen->resetClipBounds();
_vm->_game->_scene.restrictScene();
}
@@ -491,16 +488,13 @@ void FullScreenDialog::display() {
game._trigger = 0;
// Clear the screen and draw the upper and lower horizontal lines
- _vm->_screen.empty();
+ _vm->_screen->clear();
_vm->_palette->setLowRange();
- _vm->_screen.hLine(0, 20, MADS_SCREEN_WIDTH, 2);
- _vm->_screen.hLine(0, 179, MADS_SCREEN_WIDTH, 2);
- _vm->_screen.resetClipBounds();
- _vm->_screen.copyRectToScreen(Common::Rect(0, 0, MADS_SCREEN_WIDTH, MADS_SCREEN_HEIGHT));
+ _vm->_screen->hLine(0, 20, MADS_SCREEN_WIDTH, 2);
+ _vm->_screen->hLine(0, 179, MADS_SCREEN_WIDTH, 2);
// Restrict the screen to the area between the two lines
- _vm->_screen.setClipBounds(Common::Rect(0, DIALOG_TOP, MADS_SCREEN_WIDTH,
- DIALOG_TOP + MADS_SCENE_HEIGHT));
+ _vm->_screen->setClipBounds(Common::Rect(0, DIALOG_TOP, MADS_SCREEN_WIDTH, DIALOG_TOP + MADS_SCENE_HEIGHT));
_vm->_game->_scene.restrictScene();
if (_screenId > 0)
diff --git a/engines/mads/dragonsphere/dragonsphere_scenes.cpp b/engines/mads/dragonsphere/dragonsphere_scenes.cpp
index 938931e80d..a18d03d143 100644
--- a/engines/mads/dragonsphere/dragonsphere_scenes.cpp
+++ b/engines/mads/dragonsphere/dragonsphere_scenes.cpp
@@ -218,7 +218,7 @@ void SceneInfoDragonsphere::loadCodes(MSurface &depthSurface, int variant) {
}
void SceneInfoDragonsphere::loadCodes(MSurface &depthSurface, Common::SeekableReadStream *stream) {
- byte *destP = depthSurface.getData();
+ byte *destP = (byte *)depthSurface.getPixels();
byte *walkMap = new byte[stream->size()];
stream->read(walkMap, stream->size());
diff --git a/engines/mads/events.cpp b/engines/mads/events.cpp
index 7463704c4b..de83260b0f 100644
--- a/engines/mads/events.cpp
+++ b/engines/mads/events.cpp
@@ -98,7 +98,7 @@ void EventsManager::changeCursor() {
// Check for hotspot indication pixels along the right-hand and bottom
// row. Put together, these give the cursor's hotspot x,y
int hotspotX = 0, hotspotY = 0;
- byte *cursorData = cursor->getData();
+ const byte *cursorData = (const byte *)cursor->getPixels();
for (int idx = 0; idx < cursor->w; ++idx) {
if (cursorData[(cursor->h - 1) * cursor->w + idx] != transIndex)
hotspotX = idx;
@@ -110,7 +110,7 @@ void EventsManager::changeCursor() {
// Reduce the cursor data to remove the last column from each row, since
// the cursor routines don't have a pitch option
byte *destCursor = new byte[(cursor->w - 1) * (cursor->h - 1)];
- byte *srcP = cursorData;
+ const byte *srcP = cursorData;
byte *destP = destCursor;
for (int idx = 0; idx < (cursor->h - 1); ++idx) {
@@ -217,7 +217,7 @@ bool EventsManager::checkForNextFrameCounter() {
_vm->_debugger->onFrame();
// Display the frame
- _vm->_screen.updateScreen();
+ _vm->_screen->update();
// Signal the ScummVM debugger
_vm->_debugger->onFrame();
diff --git a/engines/mads/font.cpp b/engines/mads/font.cpp
index 3e6d23fe6f..3828c3df8e 100644
--- a/engines/mads/font.cpp
+++ b/engines/mads/font.cpp
@@ -167,16 +167,13 @@ int Font::writeString(MSurface *surface, const Common::String &msg, const Common
return x;
int bottom = y + height - 1;
- if (bottom > surface->getHeight() - 1) {
- height -= MIN(height, bottom - (surface->getHeight() - 1));
+ if (bottom > surface->h - 1) {
+ height -= MIN(height, bottom - (surface->h - 1));
}
if (height <= 0)
return x;
- byte *destPtr = surface->getBasePtr(x, y);
- uint8 *oldDestPtr = destPtr;
-
int xPos = x;
const char *text = msg.c_str();
@@ -185,10 +182,11 @@ int Font::writeString(MSurface *surface, const Common::String &msg, const Common
int charWidth = _charWidths[(byte)theChar];
if (charWidth > 0) {
-
if (xPos + charWidth > xEnd)
return xPos;
+ Graphics::Surface dest = surface->getSubArea(
+ Common::Rect(xPos, y, xPos + charWidth, y + height));
uint8 *charData = &_charData[_charOffs[(byte)theChar]];
int bpp = getBpp(charWidth);
@@ -196,6 +194,8 @@ int Font::writeString(MSurface *surface, const Common::String &msg, const Common
charData += bpp * skipY;
for (int i = 0; i < height; i++) {
+ byte *destPtr = (byte *)dest.getBasePtr(0, i);
+
for (int j = 0; j < bpp; j++) {
if (*charData & 0xc0)
*destPtr = _fontColors[(*charData & 0xc0) >> 6];
@@ -211,22 +211,13 @@ int Font::writeString(MSurface *surface, const Common::String &msg, const Common
destPtr++;
charData++;
}
-
- destPtr += surface->getWidth() - bpp * 4;
-
}
-
- destPtr = oldDestPtr + charWidth + spaceWidth;
- oldDestPtr = destPtr;
-
}
xPos += charWidth + spaceWidth;
-
}
return xPos;
-
}
int Font::getWidth(const Common::String &msg, int spaceWidth) {
diff --git a/engines/mads/game.cpp b/engines/mads/game.cpp
index 8ebea2a3b2..0a6741ba7a 100644
--- a/engines/mads/game.cpp
+++ b/engines/mads/game.cpp
@@ -498,7 +498,7 @@ void Game::loadGame(int slotNumber) {
_scene._currentSceneId = -2;
_sectionNumber = _scene._nextSceneId / 100;
_scene._frameStartTime = _vm->_events->getFrameCounter();
- _vm->_screen._shakeCountdown = -1;
+ _vm->_screen->_shakeCountdown = -1;
// Default the selected inventory item to the first one, if the player has any
_scene._userInterface._selectedInvIndex = _objects._inventoryList.size() > 0 ? 0 : -1;
@@ -600,7 +600,8 @@ void Game::createThumbnail() {
uint8 thumbPalette[PALETTE_SIZE];
_vm->_palette->grabPalette(thumbPalette, 0, PALETTE_COUNT);
_saveThumb = new Graphics::Surface();
- ::createThumbnail(_saveThumb, _vm->_screen.getData(), MADS_SCREEN_WIDTH, MADS_SCREEN_HEIGHT, thumbPalette);
+ ::createThumbnail(_saveThumb, (const byte *)_vm->_screen->getPixels(),
+ MADS_SCREEN_WIDTH, MADS_SCREEN_HEIGHT, thumbPalette);
}
void Game::syncTimers(SyncType slaveType, int slaveId, SyncType masterType, int masterId) {
diff --git a/engines/mads/mads.cpp b/engines/mads/mads.cpp
index 963a35c24c..29bcd10094 100644
--- a/engines/mads/mads.cpp
+++ b/engines/mads/mads.cpp
@@ -94,22 +94,15 @@ void MADSEngine::initialize() {
_palette = new Palette(this);
Font::init(this);
_font = new Font();
- _screen.init();
+ _screen = new Screen();
_sound = new SoundManager(this, _mixer);
_audio = new AudioPlayer(_mixer, getGameID());
_game = Game::init(this);
-
- switch (getGameID()) {
- case GType_RexNebular:
- _gameConv = nullptr;
- break;
- default:
- _gameConv = new GameConversations(this);
- }
+ _gameConv = new GameConversations(this);
loadOptions();
- _screen.empty();
+ _screen->clear();
}
void MADSEngine::loadOptions() {
diff --git a/engines/mads/mads.h b/engines/mads/mads.h
index eb808de32f..52f71f7c79 100644
--- a/engines/mads/mads.h
+++ b/engines/mads/mads.h
@@ -100,7 +100,7 @@ public:
GameConversations * _gameConv;
Palette *_palette;
Resources *_resources;
- ScreenSurface _screen;
+ Screen *_screen;
SoundManager *_sound;
AudioPlayer *_audio;
bool _easyMouse;
diff --git a/engines/mads/menu_views.cpp b/engines/mads/menu_views.cpp
index 10d5a2179a..9050ca6081 100644
--- a/engines/mads/menu_views.cpp
+++ b/engines/mads/menu_views.cpp
@@ -253,7 +253,7 @@ void TextView::processCommand() {
SceneInfo *sceneInfo = SceneInfo::init(_vm);
sceneInfo->_width = MADS_SCREEN_WIDTH;
sceneInfo->_height = MADS_SCENE_HEIGHT;
- _spareScreens[spareIndex].setSize(MADS_SCREEN_WIDTH, MADS_SCENE_HEIGHT);
+ _spareScreens[spareIndex].create(MADS_SCREEN_WIDTH, MADS_SCENE_HEIGHT);
sceneInfo->loadMadsV1Background(screenId, "", SCENEFLAG_TRANSLATE,
_spareScreens[spareIndex]);
@@ -346,9 +346,11 @@ void TextView::doFrame() {
// If a screen transition is in progress and it's time for another column, handle it
if (_spareScreen) {
- byte *srcP = _spareScreen->getBasePtr(_translationX, 0);
- byte *bgP = scene._backgroundSurface.getBasePtr(_translationX, 0);
- byte *screenP = (byte *)_vm->_screen.getBasePtr(_translationX, 0);
+ const byte *srcP = (const byte *)_spareScreen->getBasePtr(_translationX, 0);
+ byte *bgP = (byte *)scene._backgroundSurface.getBasePtr(_translationX, 0);
+
+ Graphics::Surface dest = _vm->_screen->getSubArea(Common::Rect(_translationX, 0, _translationX + 1, 0));
+ byte *screenP = (byte *)dest.getBasePtr(0, 0);
for (int y = 0; y < MADS_SCENE_HEIGHT; ++y, srcP += MADS_SCREEN_WIDTH,
bgP += MADS_SCREEN_WIDTH, screenP += MADS_SCREEN_WIDTH) {
@@ -356,10 +358,6 @@ void TextView::doFrame() {
*screenP = *srcP;
}
- // Flag the column of the screen is modified
- _vm->_screen.copyRectToScreen(Common::Rect(_translationX, 0,
- _translationX + 1, MADS_SCENE_HEIGHT));
-
// Keep moving the column to copy to the right
if (++_translationX == MADS_SCREEN_WIDTH) {
// Surface transition is complete
@@ -571,6 +569,7 @@ void AnimationView::doFrame() {
void AnimationView::loadNextResource() {
Scene &scene = _vm->_game->_scene;
Palette &palette = *_vm->_palette;
+ Screen &screen = *_vm->_screen;
ResourceEntry &resEntry = _resources[_resourceIndex];
Common::Array<PaletteCycle> paletteCycles;
@@ -587,12 +586,15 @@ void AnimationView::loadNextResource() {
// Handle the bars at the top/bottom
if (resEntry._showWhiteBars) {
// For animations the screen has been clipped to the middle 156 rows.
- // So although it's slightly messy, bypass our screen class entirely,
- // and draw the horizontal lines directly on the physiacl screen surface
- Graphics::Surface *s = g_system->lockScreen();
- s->hLine(0, 20, MADS_SCREEN_WIDTH, 253);
- s->hLine(0, 179, MADS_SCREEN_WIDTH, 253);
- g_system->unlockScreen();
+ // So although it's slightly messy, temporarily reset clip bounds
+ // so we can redraw the white lines
+ Common::Rect clipBounds = screen.getClipBounds();
+ screen.resetClipBounds();
+
+ screen.hLine(0, 20, MADS_SCREEN_WIDTH, 253);
+ screen.hLine(0, 179, MADS_SCREEN_WIDTH, 253);
+
+ screen.setClipBounds(clipBounds);
}
// Load the new animation
diff --git a/engines/mads/msurface.cpp b/engines/mads/msurface.cpp
index f768624278..40c69c0f08 100644
--- a/engines/mads/msurface.cpp
+++ b/engines/mads/msurface.cpp
@@ -32,37 +32,6 @@ namespace MADS {
MADSEngine *MSurface::_vm = nullptr;
-MSurface::MSurface() {
- pixels = nullptr;
- _freeFlag = false;
-}
-
-MSurface::MSurface(int width, int height) {
- pixels = nullptr;
- _freeFlag = false;
- setSize(width, height);
-}
-
-MSurface::~MSurface() {
- if (_freeFlag)
- Graphics::Surface::free();
-}
-
-void MSurface::setSize(int width, int height) {
- if (_freeFlag)
- Graphics::Surface::free();
- Graphics::Surface::create(width, height, Graphics::PixelFormat::createFormatCLUT8());
- _freeFlag = true;
-}
-
-void MSurface::setPixels(byte *pData, int horizSize, int vertSize) {
- _freeFlag = false;
- pixels = pData;
- w = pitch = horizSize;
- h = vertSize;
- format.bytesPerPixel = 1;
-}
-
int MSurface::scaleValue(int value, int scale, int err) {
int scaled = 0;
while (value--) {
@@ -76,7 +45,6 @@ int MSurface::scaleValue(int value, int scale, int err) {
}
void MSurface::drawSprite(const Common::Point &pt, SpriteInfo &info, const Common::Rect &clipRect) {
-
enum {
kStatusSkip,
kStatusScale,
@@ -116,8 +84,8 @@ void MSurface::drawSprite(const Common::Point &pt, SpriteInfo &info, const Commo
return;
int heightAmt = scaledHeight;
- byte *src = info.sprite->getData();
- byte *dst = getBasePtr(x - info.hotX - clipX, y - info.hotY - clipY);
+ const byte *src = (const byte *)info.sprite->getPixels();
+ byte *dst = (byte *)getBasePtr(x - info.hotX - clipX, y - info.hotY - clipY);
int status = kStatusSkip;
byte *scaledLineBuf = new byte[scaledWidth];
@@ -138,7 +106,7 @@ void MSurface::drawSprite(const Common::Point &pt, SpriteInfo &info, const Commo
byte *lineDst = scaledLineBuf;
int curErrX = errX;
int width = scaledWidth;
- byte *tempSrc = src;
+ const byte *tempSrc = src;
int startX = clipX;
while (width > 0) {
byte pixel = *tempSrc++;
@@ -201,63 +169,136 @@ void MSurface::drawSprite(const Common::Point &pt, SpriteInfo &info, const Commo
}
delete[] scaledLineBuf;
-
}
-void MSurface::empty() {
- Common::fill(getBasePtr(0, 0), getBasePtr(0, h), 0);
+void MSurface::scrollX(int xAmount) {
+ if (xAmount == 0)
+ return;
+
+ byte buffer[80];
+ int direction = (xAmount > 0) ? -1 : 1;
+ int xSize = ABS(xAmount);
+ assert(xSize <= 80);
+
+ byte *srcP = (byte *)getBasePtr(0, 0);
+
+ for (int y = 0; y < this->h; ++y, srcP += pitch) {
+ if (direction < 0) {
+ // Copy area to be overwritten
+ Common::copy(srcP, srcP + xSize, &buffer[0]);
+ // Shift the remainder of the line over the given area
+ Common::copy(srcP + xSize, srcP + this->w, srcP);
+ // Move buffered area to the end of the line
+ Common::copy(&buffer[0], &buffer[xSize], srcP + this->w - xSize);
+ } else {
+ // Copy area to be overwritten
+ Common::copy_backward(srcP + this->w - xSize, srcP + this->w, &buffer[80]);
+ // Shift the remainder of the line over the given area
+ Common::copy_backward(srcP, srcP + this->w - xSize, srcP + this->w);
+ // Move buffered area to the start of the line
+ Common::copy_backward(&buffer[80 - xSize], &buffer[80], srcP + xSize);
+ }
+ }
+
+ markAllDirty();
}
-void MSurface::copyFrom(MSurface *src, const Common::Rect &srcBounds,
- const Common::Point &destPos, int transparentColor) {
- // Validation of the rectangle and position
- int destX = destPos.x, destY = destPos.y;
- if ((destX >= w) || (destY >= h))
+void MSurface::scrollY(int yAmount) {
+ if (yAmount == 0)
return;
- Common::Rect copyRect = srcBounds;
- if (destX < 0) {
- copyRect.left += -destX;
- destX = 0;
- } else if (destX + copyRect.width() > w) {
- copyRect.right -= destX + copyRect.width() - w;
- }
- if (destY < 0) {
- copyRect.top += -destY;
- destY = 0;
- } else if (destY + copyRect.height() > h) {
- copyRect.bottom -= destY + copyRect.height() - h;
+ int direction = (yAmount > 0) ? 1 : -1;
+ int ySize = ABS(yAmount);
+ assert(ySize < (this->h / 2));
+ assert(this->w == pitch);
+
+ int blockSize = ySize * this->w;
+ byte *tempData = new byte[blockSize];
+ byte *pixelsP = (byte *)getBasePtr(0, 0);
+
+ if (direction > 0) {
+ // Buffer the lines to be overwritten
+ byte *srcP = (byte *)getBasePtr(0, this->h - ySize);
+ Common::copy(srcP, srcP + (pitch * ySize), tempData);
+ // Vertically shift all the lines
+ Common::copy_backward(pixelsP, pixelsP + (pitch * (this->h - ySize)),
+ pixelsP + (pitch * this->h));
+ // Transfer the buffered lines top the top of the screen
+ Common::copy(tempData, tempData + blockSize, pixelsP);
+ } else {
+ // Buffer the lines to be overwritten
+ Common::copy(pixelsP, pixelsP + (pitch * ySize), tempData);
+ // Vertically shift all the lines
+ Common::copy(pixelsP + (pitch * ySize), pixelsP + (pitch * this->h), pixelsP);
+ // Transfer the buffered lines to the bottom of the screen
+ Common::copy(tempData, tempData + blockSize, pixelsP + (pitch * (this->h - ySize)));
}
- if (!copyRect.isValidRect())
- return;
+ markAllDirty();
+ delete[] tempData;
+}
- // Copy the specified area
+void MSurface::translate(Common::Array<RGB6> &palette) {
+ for (int y = 0; y < this->h; ++y) {
+ byte *pDest = (byte *)getBasePtr(0, y);
+
+ for (int x = 0; x < this->w; ++x, ++pDest) {
+ if (*pDest < 255) // scene 752 has some palette indices of 255
+ *pDest = palette[*pDest]._palIndex;
+ }
+ }
- byte *data = src->getData();
- byte *srcPtr = data + (src->getWidth() * copyRect.top + copyRect.left);
- byte *destPtr = (byte *)pixels + (destY * getWidth()) + destX;
+ markAllDirty();
+}
- for (int rowCtr = 0; rowCtr < copyRect.height(); ++rowCtr) {
- if (transparentColor == -1) {
- // No transparency, so copy line over
- Common::copy(srcPtr, srcPtr + copyRect.width(), destPtr);
- } else {
- // Copy each byte one at a time checking for the transparency color
- for (int xCtr = 0; xCtr < copyRect.width(); ++xCtr)
- if (srcPtr[xCtr] != transparentColor) destPtr[xCtr] = srcPtr[xCtr];
+void MSurface::translate(byte map[PALETTE_COUNT]) {
+ for (int y = 0; y < this->h; ++y) {
+ byte *pDest = (byte *)getBasePtr(0, y);
+
+ for (int x = 0; x < this->w; ++x, ++pDest) {
+ *pDest = map[*pDest];
}
+ }
+
+ markAllDirty();
+}
+
+MSurface *MSurface::flipHorizontal() const {
+ MSurface *dest = new MSurface(this->w, this->h);
+
+ for (int y = 0; y < this->h; ++y) {
+ const byte *srcP = getBasePtr(this->w - 1, y);
+ byte *destP = dest->getBasePtr(0, y);
- srcPtr += src->getWidth();
- destPtr += getWidth();
+ for (int x = 0; x < this->w; ++x)
+ *destP++ = *srcP--;
+ }
+
+ return dest;
+}
+
+void MSurface::copyRectTranslate(MSurface &srcSurface, const byte *paletteMap,
+ const Common::Point &destPos, const Common::Rect &srcRect) {
+ // Loop through the lines
+ for (int yCtr = 0; yCtr < srcRect.height(); ++yCtr) {
+ const byte *srcP = (const byte *)srcSurface.getBasePtr(srcRect.left, srcRect.top + yCtr);
+ byte *destP = (byte *)getBasePtr(destPos.x, destPos.y + yCtr);
+
+ // Copy the line over
+ for (int xCtr = 0; xCtr < srcRect.width(); ++xCtr, ++srcP, ++destP) {
+ *destP = paletteMap[*srcP];
+ }
}
+
+ addDirtyRect(Common::Rect(destPos.x, destPos.y, destPos.x + srcRect.width(),
+ destPos.y + srcRect.height()));
}
-void MSurface::copyFrom(MSurface *src, const Common::Point &destPos, int depth,
+void MSurface::copyFrom(MSurface &src, const Common::Point &destPos, int depth,
DepthSurface *depthSurface, int scale, bool flipped, int transparentColor) {
int destX = destPos.x, destY = destPos.y;
- int frameWidth = src->getWidth();
- int frameHeight = src->getHeight();
+ int frameWidth = src.w;
+ int frameHeight = src.h;
int direction = flipped ? -1 : 1;
int highestDim = MAX(frameWidth, frameHeight);
@@ -271,7 +312,8 @@ void MSurface::copyFrom(MSurface *src, const Common::Point &destPos, int depth,
distCtr += scale;
if (distCtr < 100) {
lineDist[distIndex] = false;
- } else {
+ }
+ else {
lineDist[distIndex] = true;
distCtr -= 100;
@@ -290,18 +332,20 @@ void MSurface::copyFrom(MSurface *src, const Common::Point &destPos, int depth,
// Special case for quicker drawing of non-scaled images
if (scale == 100 || scale == -1) {
// Copy the specified area
- Common::Rect copyRect(0, 0, src->getWidth(), src->getHeight());
+ Common::Rect copyRect(0, 0, src.w, src.h);
if (destX < 0) {
copyRect.left += -destX;
destX = 0;
- } else if (destX + copyRect.width() > w) {
+ }
+ else if (destX + copyRect.width() > w) {
copyRect.right -= destX + copyRect.width() - w;
}
if (destY < 0) {
copyRect.top += -destY;
destY = 0;
- } else if (destY + copyRect.height() > h) {
+ }
+ else if (destY + copyRect.height() > h) {
copyRect.bottom -= destY + copyRect.height() - h;
}
@@ -311,9 +355,9 @@ void MSurface::copyFrom(MSurface *src, const Common::Point &destPos, int depth,
if (flipped)
copyRect.moveTo(0, copyRect.top);
- byte *data = src->getData();
- byte *srcPtr = data + (src->getWidth() * copyRect.top + copyRect.left);
- byte *destPtr = (byte *)pixels + (destY * pitch) + destX;
+ byte *data = src.getPixels();
+ byte *srcPtr = data + (src.w * copyRect.top + copyRect.left);
+ byte *destPtr = (byte *)getPixels() + (destY * pitch) + destX;
if (flipped)
srcPtr += copyRect.width() - 1;
@@ -329,18 +373,18 @@ void MSurface::copyFrom(MSurface *src, const Common::Point &destPos, int depth,
destPtr[xCtr] = *srcP;
}
- srcPtr += src->getWidth();
- destPtr += getWidth();
+ srcPtr += src.w;
+ destPtr += this->w;
}
return;
}
// Start of draw logic for scaled sprites
- const byte *srcPixelsP = src->getData();
+ const byte *srcPixelsP = src.getPixels();
- int destRight = this->getWidth() - 1;
- int destBottom = this->getHeight() - 1;
+ int destRight = this->w - 1;
+ int destBottom = this->h - 1;
// Check x bounding area
int spriteLeft = 0;
@@ -387,7 +431,7 @@ void MSurface::copyFrom(MSurface *src, const Common::Point &destPos, int depth,
spriteLeft = spriteLeft * direction;
// Loop through the lines of the sprite
- for (int yp = 0, sprY = -1; yp < frameHeight; ++yp, srcPixelsP += src->pitch) {
+ for (int yp = 0, sprY = -1; yp < frameHeight; ++yp, srcPixelsP += src.pitch) {
if (!lineDist[yp])
// Not a display line, so skip it
continue;
@@ -411,8 +455,8 @@ void MSurface::copyFrom(MSurface *src, const Common::Point &destPos, int depth,
continue;
// Get depth of current output pixel in depth surface
- Common::Point pt((destP - (byte *)this->pixels) % this->pitch,
- (destP - (byte *)this->pixels) / this->pitch);
+ Common::Point pt((destP - (byte *)getPixels()) % this->pitch,
+ (destP - (byte *)getPixels()) / this->pitch);
int pixelDepth = (depthSurface == nullptr) ? 15 : depthSurface->getDepth(pt);
if ((*srcP != transparentColor) && (depth <= pixelDepth))
@@ -424,119 +468,8 @@ void MSurface::copyFrom(MSurface *src, const Common::Point &destPos, int depth,
// Move to the next destination line
destPixelsP += this->pitch;
}
-}
-
-void MSurface::scrollX(int xAmount) {
- if (xAmount == 0)
- return;
-
- byte buffer[80];
- int direction = (xAmount > 0) ? -1 : 1;
- int xSize = ABS(xAmount);
- assert(xSize <= 80);
-
- byte *srcP = getBasePtr(0, 0);
-
- for (int y = 0; y < this->h; ++y, srcP += pitch) {
- if (direction < 0) {
- // Copy area to be overwritten
- Common::copy(srcP, srcP + xSize, &buffer[0]);
- // Shift the remainder of the line over the given area
- Common::copy(srcP + xSize, srcP + this->w, srcP);
- // Move buffered area to the end of the line
- Common::copy(&buffer[0], &buffer[xSize], srcP + this->w - xSize);
- } else {
- // Copy area to be overwritten
- Common::copy_backward(srcP + this->w - xSize, srcP + this->w, &buffer[80]);
- // Shift the remainder of the line over the given area
- Common::copy_backward(srcP, srcP + this->w - xSize, srcP + this->w);
- // Move buffered area to the start of the line
- Common::copy_backward(&buffer[80 - xSize], &buffer[80], srcP + xSize);
- }
- }
-}
-
-void MSurface::scrollY(int yAmount) {
- if (yAmount == 0)
- return;
-
- int direction = (yAmount > 0) ? 1 : -1;
- int ySize = ABS(yAmount);
- assert(ySize < (this->h / 2));
- assert(this->w == pitch);
-
- int blockSize = ySize * this->w;
- byte *tempData = new byte[blockSize];
- byte *pixelsP = getBasePtr(0, 0);
-
- if (direction > 0) {
- // Buffer the lines to be overwritten
- byte *srcP = (byte *)getBasePtr(0, this->h - ySize);
- Common::copy(srcP, srcP + (pitch * ySize), tempData);
- // Vertically shift all the lines
- Common::copy_backward(pixelsP, pixelsP + (pitch * (this->h - ySize)),
- pixelsP + (pitch * this->h));
- // Transfer the buffered lines top the top of the screen
- Common::copy(tempData, tempData + blockSize, pixelsP);
- } else {
- // Buffer the lines to be overwritten
- Common::copy(pixelsP, pixelsP + (pitch * ySize), tempData);
- // Vertically shift all the lines
- Common::copy(pixelsP + (pitch * ySize), pixelsP + (pitch * this->h), pixelsP);
- // Transfer the buffered lines to the bottom of the screen
- Common::copy(tempData, tempData + blockSize, pixelsP + (pitch * (this->h - ySize)));
- }
-
- delete[] tempData;
-}
-
-void MSurface::translate(Common::Array<RGB6> &palette) {
- for (int y = 0; y < this->h; ++y) {
- byte *pDest = getBasePtr(0, y);
-
- for (int x = 0; x < this->w; ++x, ++pDest) {
- if (*pDest < 255) // scene 752 has some palette indices of 255
- *pDest = palette[*pDest]._palIndex;
- }
- }
-}
-
-void MSurface::translate(byte map[PALETTE_COUNT]) {
- for (int y = 0; y < this->h; ++y) {
- byte *pDest = getBasePtr(0, y);
-
- for (int x = 0; x < this->w; ++x, ++pDest) {
- *pDest = map[*pDest];
- }
- }
-}
-
-MSurface *MSurface::flipHorizontal() const {
- MSurface *dest = new MSurface(this->w, this->h);
-
- for (int y = 0; y < this->h; ++y) {
- const byte *srcP = getBasePtr(this->w - 1, y);
- byte *destP = dest->getBasePtr(0, y);
-
- for (int x = 0; x < this->w; ++x)
- *destP++ = *srcP--;
- }
-
- return dest;
-}
-
-void MSurface::copyRectTranslate(MSurface &srcSurface, const byte *paletteMap,
- const Common::Point &destPos, const Common::Rect &srcRect) {
- // Loop through the lines
- for (int yCtr = 0; yCtr < srcRect.height(); ++yCtr) {
- const byte *srcP = srcSurface.getBasePtr(srcRect.left, srcRect.top + yCtr);
- byte *destP = getBasePtr(destPos.x, destPos.y + yCtr);
- // Copy the line over
- for (int xCtr = 0; xCtr < srcRect.width(); ++xCtr, ++srcP, ++destP) {
- *destP = paletteMap[*srcP];
- }
- }
+ addDirtyRect(Common::Rect(destX, destY, destX + frameWidth, destY + frameHeight));
}
/*------------------------------------------------------------------------*/
@@ -544,26 +477,26 @@ void MSurface::copyRectTranslate(MSurface &srcSurface, const byte *paletteMap,
int DepthSurface::getDepth(const Common::Point &pt) {
if (_depthStyle == 2) {
int bits = (3 - (pt.x % 4)) * 2;
- byte v = *getBasePtr(pt.x >> 2, pt.y);
+ byte v = *(const byte *)getBasePtr(pt.x >> 2, pt.y);
return v >> bits;
} else {
if (pt.x < 0 || pt.y < 0 || pt.x >= this->w || pt.y >= this->h)
return 0;
- return *getBasePtr(pt.x, pt.y) & 0xF;
+ return *(const byte *)getBasePtr(pt.x, pt.y) & 0xF;
}
}
int DepthSurface::getDepthHighBit(const Common::Point &pt) {
if (_depthStyle == 2) {
int bits = (3 - (pt.x % 4)) * 2;
- byte v = *getBasePtr(pt.x >> 2, pt.y);
+ byte v = *(const byte *)getBasePtr(pt.x >> 2, pt.y);
return (v >> bits) & 2;
} else {
if (pt.x < 0 || pt.y < 0 || pt.x >= this->w || pt.y >= this->h)
return 0;
- return *getBasePtr(pt.x, pt.y) & 0x80;
+ return *(const byte *)getBasePtr(pt.x, pt.y) & 0x80;
}
}
diff --git a/engines/mads/msurface.h b/engines/mads/msurface.h
index 80891afb83..e92770900d 100644
--- a/engines/mads/msurface.h
+++ b/engines/mads/msurface.h
@@ -25,7 +25,7 @@
#include "common/scummsys.h"
#include "common/rect.h"
-#include "graphics/surface.h"
+#include "graphics/managed_surface.h"
#include "mads/palette.h"
namespace MADS {
@@ -50,22 +50,21 @@ struct SpriteInfo {
/*
* MADS graphics surface
*/
-class MSurface : public Graphics::Surface {
+class MSurface : virtual public Graphics::ManagedSurface {
+private:
+ /**
+ * Helper method for calculating new dimensions when scaling a sprite
+ */
+ int scaleValue(int value, int scale, int err);
protected:
static MADSEngine *_vm;
- bool _freeFlag;
public:
/**
- * Sets the engine refrence used all surfaces
+ * Sets the engine reference used all surfaces
*/
static void setVm(MADSEngine *vm) { _vm = vm; }
/**
- * Helper method for calculating new dimensions when scaling a sprite
- */
- static int scaleValue(int value, int scale, int err);
-
- /**
* Base method for descendents to load their contents
*/
virtual void load(const Common::String &resName) {}
@@ -73,126 +72,50 @@ public:
/**
* Basic constructor
*/
- MSurface();
+ MSurface() : Graphics::ManagedSurface() {}
/**
* Constructor for a surface with fixed dimensions
*/
- MSurface(int width, int height);
+ MSurface(int width, int height) : Graphics::ManagedSurface(width, height) {}
/**
* Destructor
*/
- virtual ~MSurface();
-
- /**
- * Reinitializes a surface to have a given set of dimensions
- */
- void setSize(int width, int height);
-
- /**
- * Sets the pixels the surface is associated with
- * @remarks The surface will not free the data block
- */
- void setPixels(byte *pData, int horizSize, int vertSize);
-
- /**
- * Draws an arbitrary line on the screen using a specified color
- * @param startPos Starting position
- * @param endPos Ending position
- * @param color Color to use
- */
- void line(const Common::Point &startPos, const Common::Point &endPos, byte color);
-
- /**
- * Draws a sprite
- * @param pt Position to draw sprite at
- * @param info General sprite details
- * @param clipRect Clipping rectangle to constrain sprite drawing within
- */
- void drawSprite(const Common::Point &pt, SpriteInfo &info, const Common::Rect &clipRect);
-
- /**
- * Returns the width of the surface
- */
- int getWidth() const { return w; }
-
- /**
- * Returns the height of the surface
- */
- int getHeight() const { return h; }
+ virtual ~MSurface() {}
/**
- * Returns the size of the surface as a Rect
+ * Return a rect containing the bounds of the surface
*/
- Common::Rect getBounds() const {
- return Common::Rect(0, 0, w, h);
- }
+ Common::Rect getBounds() { return Common::Rect(0, 0, this->w, this->h); }
/**
- * Returns a pointer to the surface data
+ * Return the pixels for the surface
*/
- byte *getData() { return (byte *)Graphics::Surface::getPixels(); }
+ inline byte *getPixels() { return (byte *)Graphics::ManagedSurface::getPixels(); }
/**
- * Returns a pointer to a given position within the surface
+ * Return the pixels for the surface
*/
- byte *getBasePtr(int x, int y) { return (byte *)Graphics::Surface::getBasePtr(x, y); }
+ inline const void *getPixels() const { return (const byte *)Graphics::ManagedSurface::getPixels(); }
/**
- * Returns a pointer to a given position within the surface
- */
- const byte *getBasePtr(int x, int y) const { return (const byte *)Graphics::Surface::getBasePtr(x, y); }
-
- /**
- * Clears the surface
- */
- void empty();
-
- /**
- * Copys a sub-section of another surface into the current one.
- * @param src Source surface
- * @param srcBounds Area of source surface to copy
- * @param destPos Destination position to draw in current surface
- * @param transparentColor Transparency palette index
- */
- void copyFrom(MSurface *src, const Common::Rect &srcBounds, const Common::Point &destPos,
- int transparentColor = -1);
-
- /**
- * Copys a sub-section of another surface into the current one.
- * @param src Source surface
- * @param destPos Destination position to draw in current surface
- * @param depth Depth of sprite
- * @param depthSurface Depth surface to use with sprite depth
- * @param scale Scale for image
- * @param flipped Flag for whether image is to be flipped
- * @param transparentColor Transparency palette index
- */
- void copyFrom(MSurface *src, const Common::Point &destPos, int depth, DepthSurface *depthSurface,
- int scale, bool flipped, int transparentColor = -1);
-
- /**
- * Copies the surface to a given destination surface
+ * Return a pointer to a given position on the surface
*/
- void copyTo(MSurface *dest, int transparentColor = -1) {
- dest->copyFrom(this, Common::Rect(w, h), Common::Point(), transparentColor);
- }
+ byte *getBasePtr(int x, int y) { return (byte *)Graphics::ManagedSurface::getBasePtr(x, y); }
/**
- * Copies the surface to a given destination surface
+ * Return a pointer to a given position on the surface
*/
- void copyTo(MSurface *dest, const Common::Point &pt, int transparentColor = -1) {
- dest->copyFrom(this, Common::Rect(w, h), pt, transparentColor);
- }
+ inline const byte *getBasePtr(int x, int y) const { return (const byte *)Graphics::ManagedSurface::getBasePtr(x, y); }
/**
- * Copies the surface to a given destination surface
+ * Draws a sprite
+ * @param pt Position to draw sprite at
+ * @param info General sprite details
+ * @param clipRect Clipping rectangle to constrain sprite drawing within
*/
- void copyTo(MSurface *dest, const Common::Rect &srcBounds, const Common::Point &destPos,
- int transparentColor = -1) {
- dest->copyFrom(this, srcBounds, destPos, transparentColor);
- }
+ void drawSprite(const Common::Point &pt, SpriteInfo &info, const Common::Rect &clipRect);
/**
* Scroll the screen horizontally by a given amount
@@ -227,6 +150,19 @@ public:
*/
void copyRectTranslate(MSurface &srcSurface, const byte *paletteMap,
const Common::Point &destPos, const Common::Rect &srcRect);
+
+ /**
+ * Copys a sub-section of another surface into the current one.
+ * @param src Source surface
+ * @param destPos Destination position to draw in current surface
+ * @param depth Depth of sprite
+ * @param depthSurface Depth surface to use with sprite depth
+ * @param scale Scale for image
+ * @param flipped Flag for whether image is to be flipped
+ * @param transparentColor Transparency palette index
+ */
+ void copyFrom(MSurface &src, const Common::Point &destPos, int depth, DepthSurface *depthSurface,
+ int scale, bool flipped, int transparentColor = -1);
};
class DepthSurface : public MSurface {
@@ -239,7 +175,7 @@ public:
/**
* Constructor
*/
- DepthSurface() : _depthStyle(0) {}
+ DepthSurface() : MSurface(), _depthStyle(0) {}
/**
* Returns the depth at a given position
diff --git a/engines/mads/nebular/dialogs_nebular.cpp b/engines/mads/nebular/dialogs_nebular.cpp
index 58e60fe323..2af80f517e 100644
--- a/engines/mads/nebular/dialogs_nebular.cpp
+++ b/engines/mads/nebular/dialogs_nebular.cpp
@@ -438,11 +438,10 @@ void CopyProtectionDialog::show() {
Common::KeyState curKey;
const Common::Rect inputArea(110, 165, 210, 175);
MSurface *origInput = new MSurface(inputArea.width(), inputArea.height());
- _vm->_screen.frameRect(inputArea, TEXTDIALOG_BLACK);
- _vm->_screen.copyTo(origInput, inputArea, Common::Point(0, 0));
- _font->setColors(TEXTDIALOG_FE, TEXTDIALOG_FE, TEXTDIALOG_FE, TEXTDIALOG_FE);
- _vm->_screen.copyRectToScreen(inputArea);
- _vm->_screen.updateScreen();
+ _vm->_screen->frameRect(inputArea, TEXTDIALOG_BLACK);
+ origInput->blitFrom(*_vm->_screen, inputArea, Common::Point(0, 0));
+ _font->setColors(TEXTDIALOG_FE, TEXTDIALOG_FE, TEXTDIALOG_FE, TEXTDIALOG_FE);
+ _vm->_screen->update();
bool firstTime = true;
@@ -470,11 +469,10 @@ void CopyProtectionDialog::show() {
_textInput = _hogEntry._word[0];
}
- _vm->_screen.copyFrom(origInput, Common::Rect(0, 0, inputArea.width(), inputArea.height()), Common::Point(inputArea.left, inputArea.top));
- _font->writeString(&_vm->_screen, _textInput,
+ _vm->_screen->blitFrom(*origInput, Common::Point(inputArea.left, inputArea.top));
+ _font->writeString(_vm->_screen, _textInput,
Common::Point(inputArea.left + 2, inputArea.top + 1), 1);
- _vm->_screen.copyRectToScreen(inputArea);
- _vm->_screen.updateScreen();
+ _vm->_screen->update();
}
origInput->free();
@@ -537,7 +535,7 @@ void PictureDialog::save() {
// Save the entire screen
_savedSurface = new MSurface(MADS_SCREEN_WIDTH, MADS_SCREEN_HEIGHT);
- _vm->_screen.copyTo(_savedSurface);
+ _savedSurface->blitFrom(*_vm->_screen);
// Save palette information
Common::copy(&palette._mainPalette[0], &palette._mainPalette[PALETTE_SIZE], &_palette[0]);
@@ -568,7 +566,7 @@ void PictureDialog::save() {
// Remap the greyed out screen to use the small greyscale range
// at the top end of the palette
- _vm->_screen.translate(map);
+ _vm->_screen->translate(map);
// Load the inventory picture
Common::String setName = Common::String::format("*OB%.3d.SS", _objectId);
@@ -578,13 +576,12 @@ void PictureDialog::save() {
// Get the inventory frame, and adjust the dialog position to allow for it
MSprite *frame = asset->getFrame(0);
_position.y = frame->h + 12;
- if ((_position.y + _height) > _vm->_screen.getHeight())
- _position.y -= (_position.y + _height) - _vm->_screen.getHeight();
+ if ((_position.y + _height) > _vm->_screen->h)
+ _position.y -= (_position.y + _height) - _vm->_screen->h;
// Draw the inventory picture
- frame->copyTo(&_vm->_screen, Common::Point(160 - frame->w / 2, 6),
+ _vm->_screen->transBlitFrom(*frame, Common::Point(160 - frame->w / 2, 6),
frame->getTransparencyIndex());
- _vm->_screen.copyRectToScreen(_vm->_screen.getBounds());
// Adjust the dialog colors to use
TEXTDIALOG_CONTENT1 -= 10;
@@ -598,13 +595,11 @@ void PictureDialog::save() {
void PictureDialog::restore() {
if (_savedSurface) {
- _savedSurface->copyTo(&_vm->_screen);
+ _vm->_screen->blitFrom(*_savedSurface);
_savedSurface->free();
delete _savedSurface;
_savedSurface = nullptr;
- _vm->_screen.copyRectToScreen(_vm->_screen.getBounds());
-
// Restore palette information
Palette &palette = *_vm->_palette;
Common::copy(&_palette[0], &_palette[PALETTE_SIZE], &palette._mainPalette[0]);
@@ -691,7 +686,6 @@ void GameDialog::display() {
}
GameDialog::~GameDialog() {
- _vm->_screen.resetClipBounds();
_vm->_game->_scene._currentSceneId = RETURNING_FROM_DIALOG;
}
diff --git a/engines/mads/nebular/game_nebular.cpp b/engines/mads/nebular/game_nebular.cpp
index 9c0acf1a47..1db5eaea00 100644
--- a/engines/mads/nebular/game_nebular.cpp
+++ b/engines/mads/nebular/game_nebular.cpp
@@ -827,49 +827,51 @@ void GameNebular::step() {
if (_player._visible && _player._stepEnabled && !_player._moving &&
(_player._facing == _player._turnToFacing)) {
if (_scene._frameStartTime >= (uint32)_globals[kWalkerTiming]) {
- int randomVal = _vm->getRandomNumber(29999);
- if (_globals[kSexOfRex] == REX_MALE) {
- switch (_player._facing) {
- case FACING_SOUTHWEST:
- case FACING_SOUTHEAST:
- case FACING_NORTHWEST:
- case FACING_NORTHEAST:
- if (randomVal < 200) {
- _player.addWalker(-1, 0);
- _player.addWalker(1, 0);
- }
- break;
-
- case FACING_WEST:
- case FACING_EAST:
- if (randomVal < 500) {
- for (int count = 0; count < 10; ++count) {
+ if (_player._stopWalkers.empty()) {
+ int randomVal = _vm->getRandomNumber(29999);
+ if (_globals[kSexOfRex] == REX_MALE) {
+ switch (_player._facing) {
+ case FACING_SOUTHWEST:
+ case FACING_SOUTHEAST:
+ case FACING_NORTHWEST:
+ case FACING_NORTHEAST:
+ if (randomVal < 200) {
+ _player.addWalker(-1, 0);
_player.addWalker(1, 0);
}
- }
- break;
-
- case FACING_SOUTH:
- if (randomVal < 500) {
- for (int count = 0; count < 10; ++count) {
- _player.addWalker((randomVal < 250) ? 1 : 2, 0);
+ break;
+
+ case FACING_WEST:
+ case FACING_EAST:
+ if (randomVal < 500) {
+ for (int count = 0; count < 10; ++count) {
+ _player.addWalker(1, 0);
+ }
}
- } else if (randomVal < 750) {
- for (int count = 0; count < 5; ++count) {
- _player.addWalker(1, 0);
+ break;
+
+ case FACING_SOUTH:
+ if (randomVal < 500) {
+ for (int count = 0; count < 10; ++count) {
+ _player.addWalker((randomVal < 250) ? 1 : 2, 0);
+ }
+ } else if (randomVal < 750) {
+ for (int count = 0; count < 5; ++count) {
+ _player.addWalker(1, 0);
+ }
+
+ _player.addWalker(0, 0);
+ _player.addWalker(0, 0);
+
+ for (int count = 0; count < 5; ++count) {
+ _player.addWalker(2, 0);
+ }
}
+ break;
- _player.addWalker(0, 0);
- _player.addWalker(0, 0);
-
- for (int count = 0; count < 5; ++count) {
- _player.addWalker(2, 0);
- }
+ default:
+ break;
}
- break;
-
- default:
- break;
}
}
diff --git a/engines/mads/nebular/menu_nebular.cpp b/engines/mads/nebular/menu_nebular.cpp
index 0520294b29..cd81efe0f0 100644
--- a/engines/mads/nebular/menu_nebular.cpp
+++ b/engines/mads/nebular/menu_nebular.cpp
@@ -384,8 +384,8 @@ void AdvertView::show() {
// Load the advert background onto the screen
SceneInfo *sceneInfo = SceneInfo::init(_vm);
sceneInfo->load(screenId, 0, Common::String(), 0, _vm->_game->_scene._depthSurface,
- _vm->_screen);
- _vm->_screen.copyRectToScreen(_vm->_screen.getBounds());
+ *_vm->_screen);
+ _vm->_screen->markAllDirty();
_vm->_palette->setFullPalette(_vm->_palette->_mainPalette);
delete sceneInfo;
diff --git a/engines/mads/nebular/nebular_scenes.cpp b/engines/mads/nebular/nebular_scenes.cpp
index da419a70a2..40228b4b7d 100644
--- a/engines/mads/nebular/nebular_scenes.cpp
+++ b/engines/mads/nebular/nebular_scenes.cpp
@@ -323,8 +323,8 @@ void SceneInfoNebular::loadCodes(MSurface &depthSurface, int variant) {
}
void SceneInfoNebular::loadCodes(MSurface &depthSurface, Common::SeekableReadStream *stream) {
- byte *destP = depthSurface.getData();
- byte *endP = depthSurface.getBasePtr(0, depthSurface.h);
+ byte *destP = (byte *)depthSurface.getPixels();
+ byte *endP = (byte *)depthSurface.getBasePtr(0, depthSurface.h);
byte runLength = stream->readByte();
while (destP < endP && runLength > 0) {
diff --git a/engines/mads/nebular/nebular_scenes3.cpp b/engines/mads/nebular/nebular_scenes3.cpp
index 0fb13a706c..7323ee893d 100644
--- a/engines/mads/nebular/nebular_scenes3.cpp
+++ b/engines/mads/nebular/nebular_scenes3.cpp
@@ -2818,7 +2818,7 @@ void Scene318::step() {
if (_internCounter >= 3600) {
_vm->_sound->command(59);
- _vm->_screen._shakeCountdown = 20;
+ _vm->_screen->_shakeCountdown = 20;
_internWalkingFl = true;
}
}
@@ -3288,22 +3288,22 @@ void Scene319::step() {
if (_animMode == 2) {
if (_animFrame == 13)
- _vm->_screen._shakeCountdown = 40;
+ _vm->_screen->_shakeCountdown = 40;
if (_animFrame == 16)
- _vm->_screen._shakeCountdown = 1;
+ _vm->_screen->_shakeCountdown = 1;
}
if (_animMode == 3) {
if (_animFrame == 11)
- _vm->_screen._shakeCountdown = 60;
+ _vm->_screen->_shakeCountdown = 60;
if (_animFrame == 18)
- _vm->_screen._shakeCountdown = 1;
+ _vm->_screen->_shakeCountdown = 1;
}
if ((_animMode == 4) && (_animFrame == 16))
- _vm->_screen._shakeCountdown = 80;
+ _vm->_screen->_shakeCountdown = 80;
if ((nextFrame >= 0) && (nextFrame != _scene->_animation[0]->getCurrentFrame())) {
_scene->_animation[0]->setCurrentFrame(nextFrame);
@@ -3326,7 +3326,7 @@ void Scene319::step() {
_animFrame = _scene->_animation[0]->getCurrentFrame();
_slacheTalkingFl = true;
- _vm->_screen._shakeCountdown = 1;
+ _vm->_screen->_shakeCountdown = 1;
for (int i = 0; i <= 1; i++) {
int oldIdx = _globals._sequenceIndexes[i];
@@ -3350,7 +3350,7 @@ void Scene319::step() {
_vm->_palette->setColorValues(0, 0, 0);
_vm->_palette->fadeOut(_vm->_palette->_mainPalette, nullptr, 18, 228,
248, 0, 1, 16);
- _vm->_screen._shakeCountdown = 1;
+ _vm->_screen->_shakeCountdown = 1;
_scene->_reloadSceneFlag = true;
break;
@@ -3731,7 +3731,7 @@ void Scene320::step() {
case 417:
case 457:
- _vm->_screen._shakeCountdown = 40;
+ _vm->_screen->_shakeCountdown = 40;
_vm->_sound->command(59);
break;
diff --git a/engines/mads/nebular/nebular_scenes5.cpp b/engines/mads/nebular/nebular_scenes5.cpp
index 3778a59eb3..ea3574b0d1 100644
--- a/engines/mads/nebular/nebular_scenes5.cpp
+++ b/engines/mads/nebular/nebular_scenes5.cpp
@@ -1603,7 +1603,7 @@ void Scene508::enter() {
_globals._sequenceIndexes[3] = _scene->_sequences.startCycle(_globals._spriteIndexes[3], false, -2);
_scene->_sequences.setDepth(_globals._sequenceIndexes[3], 8);
_globals._sequenceIndexes[5] = _scene->_sequences.startCycle(_globals._spriteIndexes[5], false, -2);
- int idx = _scene->_dynamicHotspots.add(NOUN_LASER_BEAM, VERB_WALKTO, _globals._sequenceIndexes[5], Common::Rect(0, 0, 0, 0));
+ int idx = _scene->_dynamicHotspots.add(NOUN_SPINACH_PATCH_DOLL, VERB_WALKTO, _globals._sequenceIndexes[5], Common::Rect(0, 0, 0, 0));
_scene->_dynamicHotspots.setPosition(idx, Common::Point(57, 116), FACING_NORTHEAST);
_scene->_hotspots.activate(NOUN_HOLE, false);
_scene->_hotspots.activate(NOUN_LASER_BEAM, false);
diff --git a/engines/mads/phantom/phantom_scenes.cpp b/engines/mads/phantom/phantom_scenes.cpp
index f7a7153fbe..7ef627ceeb 100644
--- a/engines/mads/phantom/phantom_scenes.cpp
+++ b/engines/mads/phantom/phantom_scenes.cpp
@@ -191,7 +191,7 @@ void SceneInfoPhantom::loadCodes(MSurface &depthSurface, int variant) {
}
void SceneInfoPhantom::loadCodes(MSurface &depthSurface, Common::SeekableReadStream *stream) {
- byte *destP = depthSurface.getData();
+ byte *destP = (byte *)depthSurface.getPixels();
byte *walkMap = new byte[stream->size()];
stream->read(walkMap, stream->size());
diff --git a/engines/mads/rails.cpp b/engines/mads/rails.cpp
index ee0ca98cd3..46d9e0ebd3 100644
--- a/engines/mads/rails.cpp
+++ b/engines/mads/rails.cpp
@@ -149,7 +149,7 @@ int Rails::scanPath(const Common::Point &srcPos, const Common::Point &destPos) {
++xDiff;
++yDiff;
- const byte *srcP = _depthSurface->getBasePtr(srcPos.x, srcPos.y);
+ const byte *srcP = (const byte *)_depthSurface->getBasePtr(srcPos.x, srcPos.y);
int index = xAmount;
// Outer loop
diff --git a/engines/mads/scene.cpp b/engines/mads/scene.cpp
index 83ab1151a9..66f56f9407 100644
--- a/engines/mads/scene.cpp
+++ b/engines/mads/scene.cpp
@@ -89,8 +89,7 @@ Scene::~Scene() {
}
void Scene::restrictScene() {
- _sceneSurface.init(MADS_SCREEN_WIDTH, MADS_SCENE_HEIGHT, MADS_SCREEN_WIDTH,
- _vm->_screen.getPixels(), Graphics::PixelFormat::createFormatCLUT8());
+ _sceneSurface.create(*_vm->_screen, Common::Rect(0, 0, MADS_SCREEN_WIDTH, MADS_SCENE_HEIGHT));
}
void Scene::clearVocab() {
@@ -517,7 +516,7 @@ void Scene::drawElements(ScreenTransition transitionType, bool surfaceFlag) {
if (_posAdjust != Common::Point(0, 0))
warning("Adjust used %d %d", _posAdjust.x, _posAdjust.y);
// Copy background for the dirty areas to the screen
- _dirtyAreas.copy(&_backgroundSurface, &_vm->_screen, _posAdjust);
+ _dirtyAreas.copy(&_backgroundSurface, _vm->_screen, _posAdjust);
// Handle dirty areas for foreground objects
_spriteSlots.setDirtyAreas();
@@ -528,11 +527,11 @@ void Scene::drawElements(ScreenTransition transitionType, bool surfaceFlag) {
_spriteSlots.drawSprites(&_sceneSurface);
// Draw text elements onto the view
- _textDisplay.draw(&_vm->_screen);
+ _textDisplay.draw(_vm->_screen);
if (transitionType) {
// Fading in the screen
- _vm->_screen.transition(transitionType, surfaceFlag);
+ _vm->_screen->transition(transitionType, surfaceFlag);
_vm->_sound->startQueuedCommands();
} else {
// Copy dirty areas to the screen
diff --git a/engines/mads/scene_data.cpp b/engines/mads/scene_data.cpp
index 7b0e64c1fe..5323178ec7 100644
--- a/engines/mads/scene_data.cpp
+++ b/engines/mads/scene_data.cpp
@@ -242,13 +242,13 @@ void SceneInfo::load(int sceneId, int variant, const Common::String &resName,
int height = _height;
if (!bgSurface.getPixels() || (bgSurface.w != width) || (bgSurface.h != height)) {
- bgSurface.setSize(width, height);
+ bgSurface.create(width, height);
}
if (_depthStyle == 2)
width >>= 2;
if (!depthSurface.getPixels()) {
- depthSurface.setSize(width, height);
+ depthSurface.create(width, height);
}
loadCodes(depthSurface, variant);
@@ -288,7 +288,7 @@ void SceneInfo::load(int sceneId, int variant, const Common::String &resName,
assert(asset && _depthStyle != 2);
MSprite *spr = asset->getFrame(si._frameNumber);
- bgSurface.copyFrom(spr, si._position, si._depth, &depthSurface,
+ bgSurface.copyFrom(*spr, si._position, si._depth, &depthSurface,
si._scale, false, spr->getTransparencyIndex());
}
@@ -455,7 +455,7 @@ void SceneInfo::loadMadsV2Background(int sceneId, const Common::String &resName,
newHeight = tileCount * tileHeight;
if (bgSurface.w != newWidth || bgSurface.h != newHeight)
- bgSurface.setSize(newWidth, newHeight);
+ bgSurface.create(newWidth, newHeight);
// --------------------------------------------------------------------------------
@@ -477,7 +477,7 @@ void SceneInfo::loadMadsV2Background(int sceneId, const Common::String &resName,
//debugCN(kDebugGraphics, "Tile: %i, compressed size: %i\n", i, compressedTileDataSize);
- newTile->empty();
+ newTile->clear();
byte *compressedTileData = new byte[compressedTileDataSize];
@@ -503,7 +503,8 @@ void SceneInfo::loadMadsV2Background(int sceneId, const Common::String &resName,
TileSetIterator tile = tileSet.begin();
for (int i = 0; i < tileIndex; i++)
++tile;
- ((*tile).get())->copyTo(&bgSurface, Common::Point(x * tileWidth, y * tileHeight));
+
+ bgSurface.blitFrom(*(*tile).get(), Common::Point(x * tileWidth, y * tileHeight));
((*tile).get())->free();
}
}
diff --git a/engines/mads/screen.cpp b/engines/mads/screen.cpp
index 90fbbe7e2a..05f9de61e2 100644
--- a/engines/mads/screen.cpp
+++ b/engines/mads/screen.cpp
@@ -69,7 +69,6 @@ void DirtyArea::setArea(int width, int height, int maxWidth, int maxHeight) {
_active = true;
}
-
void DirtyArea::setSpriteSlot(const SpriteSlot *spriteSlot) {
int width, height;
Scene &scene = _vm->_game->_scene;
@@ -215,12 +214,13 @@ void DirtyAreas::copy(MSurface *srcSurface, MSurface *destSurface, const Common:
Common::Point destPos(srcBounds.left, srcBounds.top);
if ((*this)[i]._active && bounds.isValidRect()) {
- srcSurface->copyTo(destSurface, bounds, destPos);
+ destSurface->blitFrom(*srcSurface, bounds, destPos);
}
}
}
void DirtyAreas::copyToScreen() {
+/*
for (uint i = 0; i < size(); ++i) {
const Common::Rect &bounds = (*this)[i]._bounds;
@@ -229,9 +229,10 @@ void DirtyAreas::copyToScreen() {
continue;
if ((*this)[i]._active && (*this)[i]._bounds.isValidRect()) {
- _vm->_screen.copyRectToScreen(bounds);
+ _vm->_screen->copyRectToScreen(bounds);
}
}
+ */
}
void DirtyAreas::reset() {
@@ -554,38 +555,17 @@ void ScreenObjects::synchronize(Common::Serializer &s) {
/*------------------------------------------------------------------------*/
-ScreenSurface::ScreenSurface() {
+Screen::Screen(): Graphics::Screen(), MSurface() {
+ // Create the screen surface separately on another surface, since the screen
+ // surface will be subject to change as the clipping area is altered
+ _rawSurface.create(MADS_SCREEN_WIDTH, MADS_SCREEN_HEIGHT);
+ resetClipBounds();
+
_shakeCountdown = -1;
_random = 0x4D2;
- _surfacePixels = nullptr;
-}
-
-void ScreenSurface::init() {
- // Set the size for the screen
- setSize(MADS_SCREEN_WIDTH, MADS_SCREEN_HEIGHT);
-
- // Store a copy of the raw pixels pointer for the screen, since the surface
- // itself may be later changed to only a subset of the screen
- _surfacePixels = (byte *)getPixels();
- _freeFlag = false;
-}
-
-ScreenSurface::~ScreenSurface() {
- ::free(_surfacePixels);
}
-void ScreenSurface::copyRectToScreen(const Common::Rect &bounds) {
- const byte *buf = getBasePtr(bounds.left, bounds.top);
-
- Common::Rect destBounds = bounds;
- destBounds.translate(_clipBounds.left, _clipBounds.top);
-
- if (bounds.width() != 0 && bounds.height() != 0)
- g_system->copyRectToScreen(buf, this->pitch, destBounds.left, destBounds.top,
- destBounds.width(), destBounds.height());
-}
-
-void ScreenSurface::updateScreen() {
+void Screen::update() {
if (_shakeCountdown >= 0) {
_random = _random * 5 + 1;
int offset = (_random >> 8) & 3;
@@ -596,27 +576,42 @@ void ScreenSurface::updateScreen() {
// offset width shown at the very right. The offset changes to give
// an effect of shaking the screen
offset *= 4;
- const byte *buf = getBasePtr(offset, 0);
- g_system->copyRectToScreen(buf, this->pitch, 0, 0,
- this->pitch - offset, this->h);
+ const byte *buf = (const byte *)getBasePtr(offset, 0);
+ g_system->copyRectToScreen(buf, this->pitch, 0, 0, this->pitch - offset, this->h);
if (offset > 0)
- g_system->copyRectToScreen(this->pixels, this->pitch,
+ g_system->copyRectToScreen(getPixels(), this->pitch,
this->pitch - offset, 0, offset, this->h);
+ return;
}
- g_system->updateScreen();
+ // Reset any clip bounds if active whilst the screen is updated
+ Common::Rect clipBounds = getClipBounds();
+ resetClipBounds();
+
+ // Update the screen
+ Graphics::Screen::update();
+
+ // Revert back to whatever clipping is active
+ setClipBounds(clipBounds);
}
-void ScreenSurface::transition(ScreenTransition transitionType, bool surfaceFlag) {
+void Screen::transition(ScreenTransition transitionType, bool surfaceFlag) {
Palette &pal = *_vm->_palette;
Scene &scene = _vm->_game->_scene;
byte palData[PALETTE_SIZE];
+ // The original loads the new scene to the screen surface for some of the
+ // transition types like fade out/in, so we need to clear the dirty rects so
+ // it doesn't prematurely get blitted to the physical screen before fade out
+ Common::Rect clipBounds = getClipBounds();
+ clearDirtyRects();
+
switch (transitionType) {
case kTransitionFadeIn:
- case kTransitionFadeOutIn:
+ case kTransitionFadeOutIn: {
Common::fill(&pal._colorValues[0], &pal._colorValues[3], 0);
Common::fill(&pal._colorFlags[0], &pal._colorFlags[3], false);
+ resetClipBounds();
if (transitionType == kTransitionFadeOutIn) {
// Fade out
@@ -628,9 +623,11 @@ void ScreenSurface::transition(ScreenTransition transitionType, bool surfaceFlag
Common::fill(&palData[0], &palData[PALETTE_SIZE], 0);
pal.setFullPalette(palData);
- copyRectToScreen(getBounds());
+ markAllDirty();
+ update();
pal.fadeIn(palData, pal._mainPalette, 0, 256, 0, 1, 1, 16);
break;
+ }
case kTransitionBoxInBottomLeft:
case kTransitionBoxInBottomRight:
@@ -666,19 +663,13 @@ void ScreenSurface::transition(ScreenTransition transitionType, bool surfaceFlag
// Quick transitions
break;
}
-}
-void ScreenSurface::setClipBounds(const Common::Rect &r) {
- _clipBounds = r;
- setPixels(_surfacePixels + pitch * r.top + r.left, r.width(), r.height());
- this->pitch = MADS_SCREEN_WIDTH;
+ // Reset clipping
+ markAllDirty();
+ setClipBounds(clipBounds);
}
-void ScreenSurface::resetClipBounds() {
- setClipBounds(Common::Rect(0, 0, MADS_SCREEN_WIDTH, MADS_SCREEN_HEIGHT));
-}
-
-void ScreenSurface::panTransition(MSurface &newScreen, byte *palData, int entrySide,
+void Screen::panTransition(MSurface &newScreen, byte *palData, int entrySide,
const Common::Point &srcPos, const Common::Point &destPos,
ThroughBlack throughBlack, bool setPalette, int numTicks) {
EventsManager &events = *_vm->_events;
@@ -735,8 +726,6 @@ void ScreenSurface::panTransition(MSurface &newScreen, byte *palData, int entryS
srcPos.x + xAt + 1, srcPos.y + size.y));
}
- copyRectToScreen(Common::Rect(xAt, destPos.y, xAt + 1, destPos.y + size.y));
-
// Slight delay
events.pollEvents();
g_system->delayMillis(1);
@@ -747,16 +736,18 @@ void ScreenSurface::panTransition(MSurface &newScreen, byte *palData, int entryS
}
if (throughBlack == THROUGH_BLACK2) {
+ /*
Common::Rect r(srcPos.x, srcPos.y, srcPos.x + size.x, srcPos.y + size.y);
copyRectToSurface(newScreen, destPos.x, destPos.y, r);
copyRectToScreen(r);
+ */
}
}
/**
* Translates the current screen from the old palette to the new palette
*/
-void ScreenSurface::swapForeground(byte newPalette[PALETTE_SIZE], byte *paletteMap) {
+void Screen::swapForeground(byte newPalette[PALETTE_SIZE], byte *paletteMap) {
Palette &palette = *_vm->_palette;
byte oldPalette[PALETTE_SIZE];
byte oldMap[PALETTE_COUNT];
@@ -775,7 +766,7 @@ void ScreenSurface::swapForeground(byte newPalette[PALETTE_SIZE], byte *paletteM
destP += 2 * RGB_SIZE;
}
- Common::Rect oldClip = _clipBounds;
+ Common::Rect oldClip = getClipBounds();
resetClipBounds();
copyRectTranslate(*this, oldMap, Common::Point(0, 0),
@@ -790,7 +781,7 @@ void ScreenSurface::swapForeground(byte newPalette[PALETTE_SIZE], byte *paletteM
* Palettes consist of 128 RGB entries for the foreground and background
* respectively, with the two interleaved together. So the start
*/
-void ScreenSurface::swapPalette(const byte *palData, byte swapTable[PALETTE_COUNT],
+void Screen::swapPalette(const byte *palData, byte swapTable[PALETTE_COUNT],
bool foreground) {
int start = foreground ? 1 : 0;
const byte *dynamicList = &palData[start * RGB_SIZE];
@@ -815,5 +806,12 @@ void ScreenSurface::swapPalette(const byte *palData, byte swapTable[PALETTE_COUN
}
}
+void Screen::setClipBounds(const Common::Rect &r) {
+ create(_rawSurface, r);
+}
+
+void Screen::resetClipBounds() {
+ setClipBounds(Common::Rect(0, 0, MADS_SCREEN_WIDTH, MADS_SCREEN_HEIGHT));
+}
} // End of namespace MADS
diff --git a/engines/mads/screen.h b/engines/mads/screen.h
index d910e88633..626080580e 100644
--- a/engines/mads/screen.h
+++ b/engines/mads/screen.h
@@ -25,6 +25,7 @@
#include "common/scummsys.h"
#include "common/array.h"
+#include "graphics/screen.h"
#include "mads/msurface.h"
#include "mads/action.h"
@@ -207,11 +208,10 @@ public:
void synchronize(Common::Serializer &s);
};
-class ScreenSurface : public MSurface {
+class Screen : virtual public Graphics::Screen, virtual public MSurface {
private:
uint16 _random;
- byte *_surfacePixels;
- Common::Rect _clipBounds;
+ MSurface _rawSurface;
void panTransition(MSurface &newScreen, byte *palData, int entrySide,
const Common::Point &srcPos, const Common::Point &destPos,
@@ -226,36 +226,40 @@ public:
/**
* Constructor
*/
- ScreenSurface();
+ Screen();
/**
* Destructor
*/
- ~ScreenSurface();
+ virtual ~Screen() {}
/**
- * Initialize the surface
+ * Updates the physical screen with contents of the internal surface
*/
- void init();
+ virtual void update();
/**
- * Copys an area of the screen surface to the ScmmVM physical screen buffer
- * @param bounds Area of screen surface to copy
+ * Transition to a new screen with a given effect
*/
- void copyRectToScreen(const Common::Rect &bounds);
+ void transition(ScreenTransition transitionType, bool surfaceFlag);
/**
- * Updates the screen with the contents of the surface
+ * Set the screen drawing area to a sub-section of the real screen
*/
- void updateScreen();
-
- void transition(ScreenTransition transitionType, bool surfaceFlag);
-
void setClipBounds(const Common::Rect &r);
+ /**
+ * Reset back to drawing on the entirety of the screen
+ */
void resetClipBounds();
- const Common::Rect &getClipBounds() { return _clipBounds; }
+ /**
+ * Return the current drawing/clip area
+ */
+ const Common::Rect getClipBounds() const {
+ const Common::Point pt = getOffsetFromOwner();
+ return Common::Rect(pt.x, pt.y, pt.x + this->w, pt.y + this->h);
+ }
};
} // End of namespace MADS
diff --git a/engines/mads/sequence.cpp b/engines/mads/sequence.cpp
index 50b37de7ea..2afe089d4a 100644
--- a/engines/mads/sequence.cpp
+++ b/engines/mads/sequence.cpp
@@ -237,8 +237,8 @@ bool SequenceList::loadSprites(int seqIndex) {
if ((seqEntry._flags != 0) || (seqEntry._dynamicHotspotIndex >= 0)) {
SpriteAsset &spriteSet = *scene._sprites[seqEntry._spritesIndex];
MSprite *frame = spriteSet.getFrame(seqEntry._frameIndex - 1);
- int width = frame->getWidth() * seqEntry._scale / 200;
- int height = frame->getHeight() * seqEntry._scale / 100;
+ int width = frame->w * seqEntry._scale / 200;
+ int height = frame->h * seqEntry._scale / 100;
Common::Point pt = spriteSlot._position;
// Handle sprite movement, if present
diff --git a/engines/mads/sprites.cpp b/engines/mads/sprites.cpp
index 0a1c0b710d..fc8ddf22d2 100644
--- a/engines/mads/sprites.cpp
+++ b/engines/mads/sprites.cpp
@@ -59,10 +59,10 @@ MSprite::MSprite() : MSurface() {
}
MSprite::MSprite(Common::SeekableReadStream *source, const Common::Array<RGB6> &palette,
- const Common::Rect &bounds)
- : MSurface(bounds.width(), bounds.height()),
- _offset(Common::Point(bounds.left, bounds.top)), _transparencyIndex(TRANSPARENT_COLOR_INDEX) {
+ const Common::Rect &bounds): MSurface(), _transparencyIndex(TRANSPARENT_COLOR_INDEX),
+ _offset(Common::Point(bounds.left, bounds.top)) {
// Load the sprite data
+ create(bounds.width(), bounds.height());
loadSprite(source, palette);
}
@@ -74,8 +74,8 @@ void MSprite::loadSprite(Common::SeekableReadStream *source,
byte *outp, *lineStart;
bool newLine = false;
- outp = getData();
- lineStart = getData();
+ outp = getPixels();
+ lineStart = getPixels();
int spriteSize = this->w * this->h;
byte transIndex = getTransparencyIndex();
Common::fill(outp, outp + spriteSize, transIndex);
@@ -84,7 +84,7 @@ void MSprite::loadSprite(Common::SeekableReadStream *source,
byte cmd1, cmd2, count, pixel;
if (newLine) {
- outp = lineStart + getWidth();
+ outp = lineStart + this->w;
lineStart = outp;
newLine = false;
}
@@ -126,7 +126,7 @@ void MSprite::loadSprite(Common::SeekableReadStream *source,
// Do a final iteration over the sprite to convert it's pixels to
// the final positions in the main palette
spriteSize = this->w * this->h;
- for (outp = getData(); spriteSize > 0; --spriteSize, ++outp) {
+ for (outp = getPixels(); spriteSize > 0; --spriteSize, ++outp) {
if (*outp != transIndex)
*outp = palette[*outp]._palIndex;
}
@@ -257,12 +257,12 @@ void SpriteSlots::drawBackground() {
}
if (spriteSlot._depth <= 1) {
- frame->copyTo(&scene._backgroundSurface, pt, frame->getTransparencyIndex());
+ scene._backgroundSurface.transBlitFrom(*frame, pt, frame->getTransparencyIndex());
} else if (scene._depthStyle == 0) {
- scene._backgroundSurface.copyFrom(frame, pt, spriteSlot._depth, &scene._depthSurface,
+ scene._backgroundSurface.copyFrom(*frame, pt, spriteSlot._depth, &scene._depthSurface,
-1, false, frame->getTransparencyIndex());
} else {
- frame->copyTo(&scene._backgroundSurface, pt, frame->getTransparencyIndex());
+ scene._backgroundSurface.transBlitFrom(*frame, pt, frame->getTransparencyIndex());
}
}
}
@@ -319,7 +319,7 @@ void SpriteSlots::drawSprites(MSurface *s) {
if ((slot._scale < 100) && (slot._scale != -1)) {
// Scaled drawing
- s->copyFrom(sprite, slot._position, slot._depth, &scene._depthSurface,
+ s->copyFrom(*sprite, slot._position, slot._depth, &scene._depthSurface,
slot._scale, flipped, sprite->getTransparencyIndex());
} else {
int xp, yp;
@@ -334,7 +334,7 @@ void SpriteSlots::drawSprites(MSurface *s) {
if (slot._depth > 1) {
// Draw the frame with depth processing
- s->copyFrom(sprite, Common::Point(xp, yp), slot._depth, &scene._depthSurface,
+ s->copyFrom(*sprite, Common::Point(xp, yp), slot._depth, &scene._depthSurface,
-1, flipped, sprite->getTransparencyIndex());
} else {
MSurface *spr = sprite;
@@ -344,7 +344,7 @@ void SpriteSlots::drawSprites(MSurface *s) {
}
// No depth, so simply draw the image
- spr->copyTo(s, Common::Point(xp, yp), sprite->getTransparencyIndex());
+ s->transBlitFrom(*spr, Common::Point(xp, yp), sprite->getTransparencyIndex());
// Free sprite if it was a flipped one
if (flipped) {
diff --git a/engines/mads/user_interface.cpp b/engines/mads/user_interface.cpp
index e4b09ff54c..8f7cb0a24b 100644
--- a/engines/mads/user_interface.cpp
+++ b/engines/mads/user_interface.cpp
@@ -112,7 +112,7 @@ void UISlots::draw(bool updateFlag, bool delFlag) {
Common::Point(dirtyArea._bounds.left, dirtyArea._bounds.top));
} else {
// Copy area
- userInterface._surface.copyTo(&userInterface, dirtyArea._bounds,
+ userInterface.blitFrom(userInterface._surface, dirtyArea._bounds,
Common::Point(dirtyArea._bounds.left, dirtyArea._bounds.top));
}
}
@@ -155,7 +155,7 @@ void UISlots::draw(bool updateFlag, bool delFlag) {
if (slot._segmentId == IMG_SPINNING_OBJECT) {
MSprite *sprite = asset->getFrame(frameNumber - 1);
- sprite->copyTo(&userInterface, slot._position,
+ userInterface.transBlitFrom(*sprite, slot._position,
sprite->getTransparencyIndex());
} else {
MSprite *sprite = asset->getFrame(frameNumber - 1);
@@ -185,7 +185,7 @@ void UISlots::draw(bool updateFlag, bool delFlag) {
// Flag area of screen as needing update
Common::Rect r = dirtyArea._bounds;
r.translate(0, scene._interfaceY);
- _vm->_screen.copyRectToScreen(r);
+ //_vm->_screen->copyRectToScreen(r);
}
}
}
@@ -339,10 +339,10 @@ UserInterface::UserInterface(MADSEngine *vm) : _vm(vm), _dirtyAreas(vm),
Common::fill(&_categoryIndexes[0], &_categoryIndexes[7], 0);
// Map the user interface to the bottom of the game's screen surface
- byte *pData = _vm->_screen.getBasePtr(0, MADS_SCENE_HEIGHT);
- setPixels(pData, MADS_SCREEN_WIDTH, MADS_INTERFACE_HEIGHT);
+ create(*_vm->_screen, Common::Rect(0, MADS_SCENE_HEIGHT, MADS_SCREEN_WIDTH,
+ MADS_SCREEN_HEIGHT));
- _surface.setSize(MADS_SCREEN_WIDTH, MADS_INTERFACE_HEIGHT);
+ _surface.create(MADS_SCREEN_WIDTH, MADS_INTERFACE_HEIGHT);
}
void UserInterface::load(const Common::String &resName) {
@@ -367,7 +367,7 @@ void UserInterface::load(const Common::String &resName) {
// Read in the surface data
Common::SeekableReadStream *pixelsStream = madsPack.getItemStream(1);
- pixelsStream->read(_surface.getData(), MADS_SCREEN_WIDTH * MADS_INTERFACE_HEIGHT);
+ pixelsStream->read(_surface.getPixels(), MADS_SCREEN_WIDTH * MADS_INTERFACE_HEIGHT);
delete pixelsStream;
}
@@ -390,7 +390,7 @@ void UserInterface::setup(InputMode inputMode) {
resName += ".INT";
load(resName);
- _surface.copyTo(this);
+ blitFrom(_surface);
}
_vm->_game->_screenObjects._inputMode = inputMode;
@@ -455,9 +455,9 @@ void UserInterface::mergeFrom(MSurface *src, const Common::Rect &srcBounds,
// Copy the specified area
- byte *data = src->getData();
- byte *srcPtr = data + (src->getWidth() * copyRect.top + copyRect.left);
- byte *destPtr = (byte *)this->pixels + (destY * getWidth()) + destX;
+ byte *data = src->getPixels();
+ byte *srcPtr = data + (src->w * copyRect.top + copyRect.left);
+ byte *destPtr = (byte *)getPixels() + (destY * this->w) + destX;
for (int rowCtr = 0; rowCtr < copyRect.height(); ++rowCtr) {
// Process each line of the area
@@ -468,8 +468,8 @@ void UserInterface::mergeFrom(MSurface *src, const Common::Rect &srcBounds,
destPtr[xCtr] = srcPtr[xCtr];
}
- srcPtr += src->getWidth();
- destPtr += getWidth();
+ srcPtr += src->w;
+ destPtr += this->w;
}
}
@@ -593,7 +593,7 @@ void UserInterface::scrollbarChanged() {
_uiSlots.add(r);
_uiSlots.draw(false, false);
drawScroller();
- updateRect(r);
+// updateRect(r);
}
void UserInterface::writeVocab(ScrCategory category, int id) {
@@ -1012,7 +1012,7 @@ void UserInterface::selectObject(int invIndex) {
_uiSlots.add(bounds);
_uiSlots.draw(false, false);
drawItemVocabList();
- updateRect(bounds);
+ //updateRect(bounds);
}
}
@@ -1036,7 +1036,7 @@ void UserInterface::updateSelection(ScrCategory category, int newIndex, int *idx
_uiSlots.add(bounds);
_uiSlots.draw(false, false);
drawInventoryList();
- updateRect(bounds);
+ //updateRect(bounds);
_inventoryChanged = false;
if (invList.size() < 2) {
@@ -1052,25 +1052,19 @@ void UserInterface::updateSelection(ScrCategory category, int newIndex, int *idx
if (oldIndex >= 0) {
writeVocab(category, oldIndex);
- if (getBounds(category, oldIndex, bounds))
- updateRect(bounds);
+/* if (getBounds(category, oldIndex, bounds))
+ updateRect(bounds); */
}
if (newIndex >= 0) {
writeVocab(category, newIndex);
- if (getBounds(category, newIndex, bounds))
- updateRect(bounds);
+/* if (getBounds(category, newIndex, bounds))
+ updateRect(bounds); */
}
}
}
-void UserInterface::updateRect(const Common::Rect &bounds) {
- Common::Rect r = bounds;
- r.translate(0, MADS_SCENE_HEIGHT);
- _vm->_screen.copyRectToScreen(r);
-}
-
void UserInterface::scrollerChanged() {
warning("TODO: scrollerChanged");
}
diff --git a/engines/mads/user_interface.h b/engines/mads/user_interface.h
index 60cc1f736d..9232dc1bb1 100644
--- a/engines/mads/user_interface.h
+++ b/engines/mads/user_interface.h
@@ -190,8 +190,6 @@ private:
* Draw a UI textual element
*/
void writeVocab(ScrCategory category, int id);
-
- void updateRect(const Common::Rect &bounds);
public:
MSurface _surface;
UISlots _uiSlots;
diff --git a/engines/metaengine.h b/engines/metaengine.h
index 41f3ec4cba..e7bfebab71 100644
--- a/engines/metaengine.h
+++ b/engines/metaengine.h
@@ -96,6 +96,9 @@ public:
/**
* Return a list of all save states associated with the given target.
*
+ * The returned list is guaranteed to be sorted by slot numbers. That
+ * means smaller slot numbers are always stored before bigger slot numbers.
+ *
* The caller has to ensure that this (Meta)Engine is responsible
* for the specified target (by using findGame on it respectively
* on the associated gameid from the relevant ConfMan entry, if present).
diff --git a/engines/mohawk/configure.engine b/engines/mohawk/configure.engine
index 47402c4560..ccb9499ef0 100644
--- a/engines/mohawk/configure.engine
+++ b/engines/mohawk/configure.engine
@@ -1,6 +1,6 @@
# This file is included from the main "configure" script
# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps]
-add_engine mohawk "Mohawk" yes "cstime myst riven" "Living Books"
+add_engine mohawk "Mohawk" yes "cstime myst riven" "Living Books" "highres"
add_engine cstime "Where in Time is Carmen Sandiego?" no
add_engine riven "Riven: The Sequel to Myst" no "" "" "16bit"
-add_engine myst "Myst" no
+add_engine myst "Myst" yes
diff --git a/engines/mohawk/console.cpp b/engines/mohawk/console.cpp
index 9b5bae78be..fd79e53b07 100644
--- a/engines/mohawk/console.cpp
+++ b/engines/mohawk/console.cpp
@@ -63,6 +63,8 @@ MystConsole::MystConsole(MohawkEngine_Myst *vm) : GUI::Debugger(), _vm(vm) {
registerCmd("disableInitOpcodes", WRAP_METHOD(MystConsole, Cmd_DisableInitOpcodes));
registerCmd("cache", WRAP_METHOD(MystConsole, Cmd_Cache));
registerCmd("resources", WRAP_METHOD(MystConsole, Cmd_Resources));
+ registerCmd("quickTest", WRAP_METHOD(MystConsole, Cmd_QuickTest));
+ registerVar("show_resource_rects", &_vm->_showResourceRects);
}
MystConsole::~MystConsole() {
@@ -119,7 +121,7 @@ static const uint16 default_start_card[12] = {
10000,
2000,
5038,
- 2, // TODO: Should be 1?
+ 1,
1,
6122,
4134,
@@ -329,6 +331,44 @@ bool MystConsole::Cmd_Resources(int argc, const char **argv) {
return true;
}
+bool MystConsole::Cmd_QuickTest(int argc, const char **argv) {
+ // Go through all the ages, all the views and click random stuff
+ for (uint i = 0; i < ARRAYSIZE(mystStackNames); i++) {
+ if (i == 2 || i == 5 || i == 9 || i == 10) continue;
+ debug("Loading stack %s", mystStackNames[i]);
+ _vm->changeToStack(i, default_start_card[i], 0, 0);
+
+ Common::Array<uint16> ids = _vm->getResourceIDList(ID_VIEW);
+ for (uint j = 0; j < ids.size(); j++) {
+ if (ids[j] == 4632) continue;
+
+ debug("Loading card %d", ids[j]);
+ _vm->changeToCard(ids[j], kTransitionCopy);
+
+ _vm->_video->updateMovies();
+ _vm->_scriptParser->runPersistentScripts();
+ _vm->_system->updateScreen();
+
+ int16 resIndex = _vm->_rnd->getRandomNumber(_vm->_resources.size()) - 1;
+ if (resIndex >= 0 && _vm->_resources[resIndex]->isEnabled()) {
+ _vm->_resources[resIndex]->handleMouseDown();
+ _vm->_resources[resIndex]->handleMouseUp();
+ }
+
+ _vm->_video->updateMovies();
+ _vm->_scriptParser->runPersistentScripts();
+ _vm->_system->updateScreen();
+
+ if (_vm->getCurStack() != i) {
+ // Clicking may have linked us to another age
+ _vm->changeToStack(i, default_start_card[i], 0, 0);
+ }
+ }
+ }
+
+ return true;
+}
+
#endif // ENABLE_MYST
#ifdef ENABLE_RIVEN
diff --git a/engines/mohawk/console.h b/engines/mohawk/console.h
index af01c0d1e0..dc40049a89 100644
--- a/engines/mohawk/console.h
+++ b/engines/mohawk/console.h
@@ -55,6 +55,7 @@ private:
bool Cmd_DisableInitOpcodes(int argc, const char **argv);
bool Cmd_Cache(int argc, const char **argv);
bool Cmd_Resources(int argc, const char **argv);
+ bool Cmd_QuickTest(int argc, const char **argv);
};
#endif
diff --git a/engines/mohawk/detection.cpp b/engines/mohawk/detection.cpp
index 926c296257..a64d7ff7df 100644
--- a/engines/mohawk/detection.cpp
+++ b/engines/mohawk/detection.cpp
@@ -26,6 +26,7 @@
#include "common/savefile.h"
#include "common/system.h"
#include "common/textconsole.h"
+#include "common/translation.h"
#include "mohawk/livingbooks.h"
@@ -35,6 +36,7 @@
#ifdef ENABLE_MYST
#include "mohawk/myst.h"
+#include "mohawk/myst_state.h"
#endif
#ifdef ENABLE_RIVEN
@@ -52,7 +54,7 @@ struct MohawkGameDescription {
};
const char* MohawkEngine::getGameId() const {
- return _gameDescription->desc.gameid;
+ return _gameDescription->desc.gameId;
}
uint32 MohawkEngine::getFeatures() const {
@@ -159,10 +161,24 @@ static const char *directoryGlobs[] = {
0
};
+static const ADExtraGuiOptionsMap optionsList[] = {
+ {
+ GAMEOPTION_PLAY_MYST_FLYBY,
+ {
+ _s("Play the Myst fly by movie"),
+ _s("The Myst fly by movie was not played by the original engine."),
+ "playmystflyby",
+ false
+ }
+ },
+
+ AD_EXTRA_GUI_OPTIONS_TERMINATOR
+};
+
class MohawkMetaEngine : public AdvancedMetaEngine {
public:
- MohawkMetaEngine() : AdvancedMetaEngine(Mohawk::gameDescriptions, sizeof(Mohawk::MohawkGameDescription), mohawkGames) {
- _singleid = "mohawk";
+ MohawkMetaEngine() : AdvancedMetaEngine(Mohawk::gameDescriptions, sizeof(Mohawk::MohawkGameDescription), mohawkGames, optionsList) {
+ _singleId = "mohawk";
_maxScanDepth = 2;
_directoryGlobs = directoryGlobs;
}
@@ -184,13 +200,18 @@ public:
virtual SaveStateList listSaves(const char *target) const;
virtual int getMaximumSaveSlot() const { return 999; }
virtual void removeSaveState(const char *target, int slot) const;
+ virtual SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
};
bool MohawkMetaEngine::hasFeature(MetaEngineFeature f) const {
return
(f == kSupportsListSaves)
|| (f == kSupportsLoadingDuringStartup)
- || (f == kSupportsDeleteSave);
+ || (f == kSupportsDeleteSave)
+ || (f == kSavesSupportMetaInfo)
+ || (f == kSavesSupportThumbnail)
+ || (f == kSavesSupportCreationDate)
+ || (f == kSavesSupportPlayTime);
}
SaveStateList MohawkMetaEngine::listSaves(const char *target) const {
@@ -198,12 +219,15 @@ SaveStateList MohawkMetaEngine::listSaves(const char *target) const {
SaveStateList saveList;
// Loading games is only supported in Myst/Riven currently.
+#ifdef ENABLE_MYST
if (strstr(target, "myst")) {
- filenames = g_system->getSavefileManager()->listSavefiles("*.mys");
+ filenames = Mohawk::MystGameState::generateSaveGameList();
for (uint32 i = 0; i < filenames.size(); i++)
saveList.push_back(SaveStateDescriptor(i, filenames[i]));
- } else if (strstr(target, "riven")) {
+ } else
+#endif
+ if (strstr(target, "riven")) {
filenames = g_system->getSavefileManager()->listSavefiles("*.rvn");
for (uint32 i = 0; i < filenames.size(); i++)
@@ -215,15 +239,35 @@ SaveStateList MohawkMetaEngine::listSaves(const char *target) const {
void MohawkMetaEngine::removeSaveState(const char *target, int slot) const {
// Removing saved games is only supported in Myst/Riven currently.
+#ifdef ENABLE_MYST
if (strstr(target, "myst")) {
- Common::StringArray filenames = g_system->getSavefileManager()->listSavefiles("*.mys");
- g_system->getSavefileManager()->removeSavefile(filenames[slot].c_str());
- } else if (strstr(target, "riven")) {
+ Common::StringArray filenames = Mohawk::MystGameState::generateSaveGameList();
+ Mohawk::MystGameState::deleteSave(filenames[slot]);
+ } else
+#endif
+ if (strstr(target, "riven")) {
Common::StringArray filenames = g_system->getSavefileManager()->listSavefiles("*.rvn");
g_system->getSavefileManager()->removeSavefile(filenames[slot].c_str());
}
}
+SaveStateDescriptor MohawkMetaEngine::querySaveMetaInfos(const char *target, int slot) const {
+#ifdef ENABLE_MYST
+ if (strstr(target, "myst")) {
+ Common::StringArray filenames = Mohawk::MystGameState::generateSaveGameList();
+
+ if (slot >= (int) filenames.size()) {
+ return SaveStateDescriptor();
+ }
+
+ return Mohawk::MystGameState::querySaveMetaInfos(filenames[slot]);
+ } else
+#endif
+ {
+ return SaveStateDescriptor();
+ }
+}
+
bool MohawkMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const {
const Mohawk::MohawkGameDescription *gd = (const Mohawk::MohawkGameDescription *)desc;
diff --git a/engines/mohawk/detection_tables.h b/engines/mohawk/detection_tables.h
index 97d2932d57..7941a0d51a 100644
--- a/engines/mohawk/detection_tables.h
+++ b/engines/mohawk/detection_tables.h
@@ -22,6 +22,13 @@
namespace Mohawk {
+#define GAMEOPTION_PLAY_MYST_FLYBY GUIO_GAMEOPTIONS1
+
+#define GUI_OPTIONS_MYST GUIO3(GUIO_NOASPECT, GUIO_NOSUBTITLES, GUIO_NOMIDI)
+#define GUI_OPTIONS_MYST_ME GUIO4(GUIO_NOASPECT, GUIO_NOSUBTITLES, GUIO_NOMIDI, GAMEOPTION_PLAY_MYST_FLYBY)
+#define GUI_OPTIONS_MYST_DEMO GUIO4(GUIO_NOASPECT, GUIO_NOSUBTITLES, GUIO_NOMIDI, GUIO_NOLAUNCHLOAD)
+#define GUI_OPTIONS_MYST_MAKING_OF GUIO4(GUIO_NOASPECT, GUIO_NOSUBTITLES, GUIO_NOMIDI, GUIO_NOLAUNCHLOAD)
+
static const MohawkGameDescription gameDescriptions[] = {
// Myst
// English Windows 3.11
@@ -34,7 +41,7 @@ static const MohawkGameDescription gameDescriptions[] = {
Common::EN_ANY,
Common::kPlatformWindows,
ADGF_UNSTABLE,
- GUIO1(GUIO_NOASPECT)
+ GUI_OPTIONS_MYST
},
GType_MYST,
0,
@@ -52,7 +59,7 @@ static const MohawkGameDescription gameDescriptions[] = {
Common::EN_ANY,
Common::kPlatformWindows,
ADGF_DEMO | ADGF_UNSTABLE,
- GUIO1(GUIO_NOASPECT)
+ GUI_OPTIONS_MYST_DEMO
},
GType_MYST,
GF_DEMO,
@@ -70,7 +77,7 @@ static const MohawkGameDescription gameDescriptions[] = {
Common::DE_DEU,
Common::kPlatformWindows,
ADGF_UNSTABLE,
- GUIO1(GUIO_NOASPECT)
+ GUI_OPTIONS_MYST
},
GType_MYST,
0,
@@ -88,7 +95,7 @@ static const MohawkGameDescription gameDescriptions[] = {
Common::DE_DEU,
Common::kPlatformWindows,
ADGF_UNSTABLE,
- GUIO1(GUIO_NOASPECT)
+ GUI_OPTIONS_MYST
},
GType_MYST,
0,
@@ -106,7 +113,7 @@ static const MohawkGameDescription gameDescriptions[] = {
Common::ES_ESP,
Common::kPlatformWindows,
ADGF_UNSTABLE,
- GUIO1(GUIO_NOASPECT)
+ GUI_OPTIONS_MYST
},
GType_MYST,
0,
@@ -124,7 +131,7 @@ static const MohawkGameDescription gameDescriptions[] = {
Common::IT_ITA,
Common::kPlatformWindows,
ADGF_UNSTABLE,
- GUIO1(GUIO_NOASPECT)
+ GUI_OPTIONS_MYST
},
GType_MYST,
0,
@@ -142,7 +149,7 @@ static const MohawkGameDescription gameDescriptions[] = {
Common::JA_JPN,
Common::kPlatformWindows,
ADGF_UNSTABLE,
- GUIO1(GUIO_NOASPECT)
+ GUI_OPTIONS_MYST
},
GType_MYST,
0,
@@ -160,7 +167,7 @@ static const MohawkGameDescription gameDescriptions[] = {
Common::FR_FRA,
Common::kPlatformWindows,
ADGF_UNSTABLE,
- GUIO1(GUIO_NOASPECT)
+ GUI_OPTIONS_MYST
},
GType_MYST,
0,
@@ -178,7 +185,7 @@ static const MohawkGameDescription gameDescriptions[] = {
Common::EN_ANY,
Common::kPlatformWindows,
ADGF_UNSTABLE,
- GUIO1(GUIO_NOASPECT)
+ GUI_OPTIONS_MYST_MAKING_OF
},
GType_MAKINGOF,
0,
@@ -196,7 +203,7 @@ static const MohawkGameDescription gameDescriptions[] = {
Common::JA_JPN,
Common::kPlatformWindows,
ADGF_UNSTABLE,
- GUIO1(GUIO_NOASPECT)
+ GUI_OPTIONS_MYST_MAKING_OF
},
GType_MAKINGOF,
0,
@@ -214,7 +221,7 @@ static const MohawkGameDescription gameDescriptions[] = {
Common::EN_ANY,
Common::kPlatformWindows,
ADGF_UNSTABLE,
- GUIO1(GUIO_NOASPECT)
+ GUI_OPTIONS_MYST_ME
},
GType_MYST,
GF_ME,
@@ -232,7 +239,7 @@ static const MohawkGameDescription gameDescriptions[] = {
Common::DE_DEU,
Common::kPlatformWindows,
ADGF_UNSTABLE,
- GUIO1(GUIO_NOASPECT)
+ GUI_OPTIONS_MYST_ME
},
GType_MYST,
GF_ME,
@@ -250,7 +257,7 @@ static const MohawkGameDescription gameDescriptions[] = {
Common::FR_FRA,
Common::kPlatformWindows,
ADGF_UNSTABLE,
- GUIO1(GUIO_NOASPECT)
+ GUI_OPTIONS_MYST_ME
},
GType_MYST,
GF_ME,
@@ -268,7 +275,7 @@ static const MohawkGameDescription gameDescriptions[] = {
Common::PL_POL,
Common::kPlatformWindows,
ADGF_UNSTABLE,
- GUIO1(GUIO_NOASPECT)
+ GUI_OPTIONS_MYST_ME
},
GType_MYST,
GF_ME,
@@ -2698,7 +2705,7 @@ static const MohawkGameDescription fallbackDescs[] = {
Common::UNK_LANG,
Common::kPlatformWindows,
ADGF_UNSTABLE,
- GUIO1(GUIO_NOASPECT)
+ GUI_OPTIONS_MYST
},
GType_MYST,
0,
@@ -2713,7 +2720,7 @@ static const MohawkGameDescription fallbackDescs[] = {
Common::UNK_LANG,
Common::kPlatformWindows,
ADGF_UNSTABLE,
- GUIO1(GUIO_NOASPECT)
+ GUI_OPTIONS_MYST_MAKING_OF
},
GType_MAKINGOF,
0,
@@ -2728,7 +2735,7 @@ static const MohawkGameDescription fallbackDescs[] = {
Common::UNK_LANG,
Common::kPlatformWindows,
ADGF_UNSTABLE,
- GUIO1(GUIO_NOASPECT)
+ GUI_OPTIONS_MYST_ME
},
GType_MYST,
GF_ME,
diff --git a/engines/mohawk/dialogs.cpp b/engines/mohawk/dialogs.cpp
index ffc455286f..6c6ae9e77f 100644
--- a/engines/mohawk/dialogs.cpp
+++ b/engines/mohawk/dialogs.cpp
@@ -24,6 +24,7 @@
#include "mohawk/dialogs.h"
#include "gui/gui-manager.h"
+#include "gui/saveload.h"
#include "gui/ThemeEngine.h"
#include "gui/widget.h"
#include "common/system.h"
@@ -82,35 +83,47 @@ enum {
kWaterCmd = 'WATR',
kDropCmd = 'DROP',
kMapCmd = 'SMAP',
- kMenuCmd = 'MENU'
+ kMenuCmd = 'MENU',
+ kSaveCmd = 'SAVE',
+ kLoadCmd = 'LOAD',
+ kQuitCmd = 'QUIT'
};
#ifdef ENABLE_MYST
-MystOptionsDialog::MystOptionsDialog(MohawkEngine_Myst* vm) : GUI::OptionsDialog("", 120, 120, 360, 200), _vm(vm) {
+MystOptionsDialog::MystOptionsDialog(MohawkEngine_Myst* vm) : GUI::Dialog(0, 0, 360, 200), _vm(vm) {
// I18N: Option for fast scene switching
- _zipModeCheckbox = new GUI::CheckboxWidget(this, 15, 10, 300, 15, _("~Z~ip Mode Activated"), 0, kZipCmd);
- _transitionsCheckbox = new GUI::CheckboxWidget(this, 15, 30, 300, 15, _("~T~ransitions Enabled"), 0, kTransCmd);
+ _zipModeCheckbox = new GUI::CheckboxWidget(this, 15, 10, 220, 15, _("~Z~ip Mode Activated"), 0, kZipCmd);
+ _transitionsCheckbox = new GUI::CheckboxWidget(this, 15, 30, 220, 15, _("~T~ransitions Enabled"), 0, kTransCmd);
// I18N: Drop book page
_dropPageButton = new GUI::ButtonWidget(this, 15, 60, 100, 25, _("~D~rop Page"), 0, kDropCmd);
// Myst ME only has maps
if (_vm->getFeatures() & GF_ME)
- _showMapButton = new GUI::ButtonWidget(this, 15, 95, 100, 25, _("~S~how Map"), 0, kMapCmd);
+ _showMapButton = new GUI::ButtonWidget(this, 15, 95, 100, 25, _("Show ~M~ap"), 0, kMapCmd);
else
_showMapButton = 0;
// Myst demo only has a menu
if (_vm->getFeatures() & GF_DEMO)
- _returnToMenuButton = new GUI::ButtonWidget(this, 15, 95, 100, 25, _("~M~ain Menu"), 0, kMenuCmd);
+ _returnToMenuButton = new GUI::ButtonWidget(this, 15, 95, 100, 25, _("Main Men~u~"), 0, kMenuCmd);
else
_returnToMenuButton = 0;
+ _loadButton = new GUI::ButtonWidget(this, 245, 25, 100, 25, _("~L~oad"), 0, kLoadCmd);
+ _saveButton = new GUI::ButtonWidget(this, 245, 60, 100, 25, _("~S~ave"), 0, kSaveCmd);
+ new GUI::ButtonWidget(this, 245, 95, 100, 25, _("~Q~uit"), 0, kQuitCmd);
+
new GUI::ButtonWidget(this, 95, 160, 120, 25, _("~O~K"), 0, GUI::kOKCmd);
new GUI::ButtonWidget(this, 225, 160, 120, 25, _("~C~ancel"), 0, GUI::kCloseCmd);
+
+ _loadDialog = new GUI::SaveLoadChooser(_("Load game:"), _("Load"), false);
+ _saveDialog = new GUI::SaveLoadChooser(_("Save game:"), _("Save"), true);
}
MystOptionsDialog::~MystOptionsDialog() {
+ delete _loadDialog;
+ delete _saveDialog;
}
void MystOptionsDialog::open() {
@@ -133,6 +146,44 @@ void MystOptionsDialog::open() {
_zipModeCheckbox->setState(_vm->_gameState->_globals.zipMode);
_transitionsCheckbox->setState(_vm->_gameState->_globals.transitions);
+
+ _loadButton->setEnabled(_vm->canLoadGameStateCurrently());
+ _saveButton->setEnabled(_vm->canSaveGameStateCurrently());
+}
+
+void MystOptionsDialog::save() {
+ int slot = _saveDialog->runModalWithCurrentTarget();
+
+ if (slot >= 0) {
+ Common::String result(_saveDialog->getResultString());
+ if (result.empty()) {
+ // If the user was lazy and entered no save name, come up with a default name.
+ result = _saveDialog->createDefaultSaveDescription(slot);
+ }
+
+ _vm->saveGameState(slot, result);
+ close();
+ }
+}
+
+void MystOptionsDialog::load() {
+ int slot = _loadDialog->runModalWithCurrentTarget();
+
+ if (slot >= 0) {
+ _vm->loadGameState(slot);
+ close();
+ }
+}
+
+void MystOptionsDialog::reflowLayout() {
+ const int screenW = g_system->getOverlayWidth();
+ const int screenH = g_system->getOverlayHeight();
+
+ // Center the dialog
+ _x = (screenW - getWidth()) / 2;
+ _y = (screenH - getHeight()) / 2;
+
+ Dialog::reflowLayout();
}
void MystOptionsDialog::handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data) {
@@ -144,18 +195,39 @@ void MystOptionsDialog::handleCommand(GUI::CommandSender *sender, uint32 cmd, ui
case kMapCmd:
_vm->_needsShowMap = true;
close();
- break;
+ break;
case kMenuCmd:
_vm->_needsShowDemoMenu = true;
close();
- break;
+ break;
+ case kLoadCmd:
+ load();
+ break;
+ case kSaveCmd:
+ save();
+ break;
+ case kQuitCmd: {
+ if (_vm->getGameType() != GType_MAKINGOF) {
+ _vm->_needsShowCredits = true;
+ } else {
+ Common::Event eventQ;
+ eventQ.type = Common::EVENT_QUIT;
+ g_system->getEventManager()->pushEvent(eventQ);
+ }
+ close();
+ }
+ break;
case GUI::kOKCmd:
_vm->_gameState->_globals.zipMode = _zipModeCheckbox->getState();
_vm->_gameState->_globals.transitions = _transitionsCheckbox->getState();
- GUI::OptionsDialog::handleCommand(sender, cmd, data);
+ setResult(1);
+ close();
+ break;
+ case GUI::kCloseCmd:
+ close();
break;
default:
- GUI::OptionsDialog::handleCommand(sender, cmd, data);
+ GUI::Dialog::handleCommand(sender, cmd, data);
}
}
diff --git a/engines/mohawk/dialogs.h b/engines/mohawk/dialogs.h
index 7470cd3acd..bc25c72a43 100644
--- a/engines/mohawk/dialogs.h
+++ b/engines/mohawk/dialogs.h
@@ -32,6 +32,10 @@
#include "gui/widget.h"
#include "gui/widgets/list.h"
+namespace GUI {
+class SaveLoadChooser;
+}
+
namespace Mohawk {
class MohawkEngine;
@@ -70,20 +74,32 @@ public:
class MohawkEngine_Myst;
-class MystOptionsDialog : public GUI::OptionsDialog {
+class MystOptionsDialog : public GUI::Dialog {
public:
MystOptionsDialog(MohawkEngine_Myst *vm);
~MystOptionsDialog();
void open();
+ virtual void reflowLayout() override;
virtual void handleCommand(GUI::CommandSender*, uint32, uint32);
private:
MohawkEngine_Myst *_vm;
+
GUI::CheckboxWidget *_zipModeCheckbox;
GUI::CheckboxWidget *_transitionsCheckbox;
+
GUI::ButtonWidget *_dropPageButton;
GUI::ButtonWidget *_showMapButton;
GUI::ButtonWidget *_returnToMenuButton;
+
+ GUI::ButtonWidget *_loadButton;
+ GUI::ButtonWidget *_saveButton;
+
+ GUI::SaveLoadChooser *_loadDialog;
+ GUI::SaveLoadChooser *_saveDialog;
+
+ void save();
+ void load();
};
#endif
diff --git a/engines/mohawk/myst.cpp b/engines/mohawk/myst.cpp
index b6a6c27329..c16fab9131 100644
--- a/engines/mohawk/myst.cpp
+++ b/engines/mohawk/myst.cpp
@@ -66,38 +66,22 @@ MohawkEngine_Myst::MohawkEngine_Myst(OSystem *syst, const MohawkGameDescription
DebugMan.addDebugChannel(kDebugHelp, "Help", "Track Help File (HELP) Parsing");
DebugMan.addDebugChannel(kDebugCache, "Cache", "Track Resource Cache Accesses");
- // Engine tweaks
- // Disabling this makes engine behavior as per
- // original, including bugs, missing bits etc. :)
- _tweaksEnabled = true;
-
_currentCursor = 0;
_mainCursor = kDefaultMystCursor;
_showResourceRects = false;
_curCard = 0;
_needsUpdate = false;
+ _canSafelySaveLoad = false;
_curResource = -1;
- _hoverResource = 0;
- _dragResource = 0;
-
- _gfx = NULL;
- _console = NULL;
- _scriptParser = NULL;
- _gameState = NULL;
- _loadDialog = NULL;
- _optionsDialog = NULL;
-
- _cursorHintCount = 0;
- _cursorHints = NULL;
-
- _prevStack = NULL;
-
- _view.conditionalImageCount = 0;
- _view.conditionalImages = NULL;
- _view.soundList = NULL;
- _view.soundListVolume = NULL;
- _view.scriptResCount = 0;
- _view.scriptResources = NULL;
+ _hoverResource = nullptr;
+
+ _gfx = nullptr;
+ _console = nullptr;
+ _scriptParser = nullptr;
+ _gameState = nullptr;
+ _optionsDialog = nullptr;
+
+ _prevStack = nullptr;
}
MohawkEngine_Myst::~MohawkEngine_Myst() {
@@ -107,20 +91,12 @@ MohawkEngine_Myst::~MohawkEngine_Myst() {
delete _console;
delete _scriptParser;
delete _gameState;
- delete _loadDialog;
delete _optionsDialog;
delete _prevStack;
delete _rnd;
- delete[] _cursorHints;
-
- delete[] _view.conditionalImages;
- delete[] _view.scriptResources;
-
for (uint32 i = 0; i < _resources.size(); i++)
delete _resources[i];
-
- _resources.clear();
}
// Uses cached data objects in preference to disk access
@@ -138,7 +114,11 @@ Common::SeekableReadStream *MohawkEngine_Myst::getResource(uint32 tag, uint16 id
}
error("Could not find a \'%s\' resource with ID %04x", tag2str(tag), id);
- return NULL;
+ return nullptr;
+}
+
+Common::Array<uint16> MohawkEngine_Myst::getResourceIDList(uint32 type) const {
+ return _mhk[0]->getResourceIDList(type);
}
void MohawkEngine_Myst::cachePreload(uint32 tag, uint16 id) {
@@ -242,7 +222,6 @@ Common::Error MohawkEngine_Myst::run() {
_gfx = new MystGraphics(this);
_console = new MystConsole(this);
_gameState = new MystGameState(this, _saveFileMan);
- _loadDialog = new GUI::SaveLoadChooser(_("Load game:"), _("Load"), false);
_optionsDialog = new MystOptionsDialog(this);
_cursor = new MystCursorManager(this);
_rnd = new Common::RandomSource("myst");
@@ -251,9 +230,9 @@ Common::Error MohawkEngine_Myst::run() {
_cursor->showCursor();
// Load game from launcher/command line if requested
- if (ConfMan.hasKey("save_slot") && canLoadGameStateCurrently()) {
+ if (ConfMan.hasKey("save_slot") && hasGameSaveSupport()) {
uint32 gameToLoad = ConfMan.getInt("save_slot");
- Common::StringArray savedGamesList = _gameState->generateSaveGameList();
+ Common::StringArray savedGamesList = MystGameState::generateSaveGameList();
if (gameToLoad > savedGamesList.size())
error ("Could not find saved game");
_gameState->load(savedGamesList[gameToLoad]);
@@ -284,7 +263,7 @@ Common::Error MohawkEngine_Myst::run() {
_needsUpdate = _video->updateMovies();
_scriptParser->runPersistentScripts();
- while (_eventMan->pollEvent(event)) {
+ while (pollEvent(event)) {
switch (event.type) {
case Common::EVENT_MOUSEMOVE: {
_needsUpdate = true;
@@ -324,17 +303,15 @@ Common::Error MohawkEngine_Myst::run() {
case Common::KEYCODE_SPACE:
pauseGame();
break;
- case Common::KEYCODE_F4:
- _showResourceRects = !_showResourceRects;
- if (_showResourceRects)
- drawResourceRects();
- break;
case Common::KEYCODE_F5:
_needsPageDrop = false;
_needsShowMap = false;
_needsShowDemoMenu = false;
+ _needsShowCredits = false;
+ _canSafelySaveLoad = true;
runDialog(*_optionsDialog);
+ _canSafelySaveLoad = false;
if (_needsPageDrop) {
dropPage();
@@ -350,6 +327,12 @@ Common::Error MohawkEngine_Myst::run() {
changeToStack(kDemoStack, 2002, 0, 0);
_needsShowDemoMenu = false;
}
+
+ if (_needsShowCredits) {
+ _cursor->hideCursor();
+ changeToStack(kCreditsStack, 10000, 0, 0);
+ _needsShowCredits = false;
+ }
break;
default:
break;
@@ -372,6 +355,15 @@ Common::Error MohawkEngine_Myst::run() {
return Common::kNoError;
}
+bool MohawkEngine_Myst::pollEvent(Common::Event &event) {
+ // Saving / Loading is allowed from the GMM only when the main event loop is running
+ _canSafelySaveLoad = true;
+ bool eventReturned = _eventMan->pollEvent(event);
+ _canSafelySaveLoad = false;
+
+ return eventReturned;
+}
+
bool MohawkEngine_Myst::skippableWait(uint32 duration) {
uint32 end = _system->getMillis() + duration;
bool skipped = false;
@@ -413,6 +405,7 @@ void MohawkEngine_Myst::changeToStack(uint16 stack, uint16 card, uint16 linkSrcS
// Fill screen with black and empty cursor
_cursor->setCursor(0);
+ _currentCursor = 0;
if (getFeatures() & GF_ME)
_system->fillScreen(_system->getScreenFormat().RGBToColor(0, 0, 0));
@@ -502,7 +495,7 @@ void MohawkEngine_Myst::changeToStack(uint16 stack, uint16 card, uint16 linkSrcS
if (getFeatures() & GF_ME) {
// Play Flyby Entry Movie on Masterpiece Edition.
- const char *flyby = 0;
+ const char *flyby = nullptr;
switch (_curStack) {
case kSeleniticStack:
@@ -512,8 +505,9 @@ void MohawkEngine_Myst::changeToStack(uint16 stack, uint16 card, uint16 linkSrcS
flyby = "stoneship flyby";
break;
// Myst Flyby Movie not used in Original Masterpiece Edition Engine
+ // We play it when first arriving on Myst, and if the user has chosen so.
case kMystStack:
- if (_tweaksEnabled)
+ if (ConfMan.getBool("playmystflyby") && card == 4134)
flyby = "myst flyby";
break;
case kMechanicalStack:
@@ -539,12 +533,12 @@ void MohawkEngine_Myst::changeToStack(uint16 stack, uint16 card, uint16 linkSrcS
uint16 MohawkEngine_Myst::getCardBackgroundId() {
uint16 imageToDraw = 0;
- if (_view.conditionalImageCount == 0)
+ if (_view.conditionalImages.size() == 0)
imageToDraw = _view.mainImage;
else {
- for (uint16 i = 0; i < _view.conditionalImageCount; i++) {
+ for (uint16 i = 0; i < _view.conditionalImages.size(); i++) {
uint16 varValue = _scriptParser->getVar(_view.conditionalImages[i].var);
- if (varValue < _view.conditionalImages[i].numStates)
+ if (varValue < _view.conditionalImages[i].values.size())
imageToDraw = _view.conditionalImages[i].values[varValue];
}
}
@@ -586,36 +580,7 @@ void MohawkEngine_Myst::changeToCard(uint16 card, TransitionType transition) {
drawCardBackground();
// Handle sound
- int16 soundAction = 0;
- uint16 soundActionVolume = 0;
-
- if (_view.sound == kMystSoundActionConditional) {
- uint16 soundVarValue = _scriptParser->getVar(_view.soundVar);
- if (soundVarValue >= _view.soundCount)
- warning("Conditional sound variable outside range");
- else {
- soundAction = _view.soundList[soundVarValue];
- soundActionVolume = _view.soundListVolume[soundVarValue];
- }
- } else {
- soundAction = _view.sound;
- soundActionVolume = _view.soundVolume;
- }
-
- if (soundAction == kMystSoundActionContinue)
- debug(2, "Continuing with current sound");
- else if (soundAction == kMystSoundActionChangeVolume) {
- debug(2, "Continuing with current sound, changing volume");
- _sound->changeBackgroundVolumeMyst(soundActionVolume);
- } else if (soundAction == kMystSoundActionStop) {
- debug(2, "Stopping sound");
- _sound->stopBackgroundMyst();
- } else if (soundAction > 0) {
- debug(2, "Playing new sound %d", soundAction);
- _sound->replaceBackgroundMyst(soundAction, soundActionVolume);
- } else {
- error("Unknown sound action %d", soundAction);
- }
+ applySoundBlock(_view.soundBlock);
if (_view.flags & kMystZipDestination)
_gameState->addZipDest(_curStack, card);
@@ -637,15 +602,16 @@ void MohawkEngine_Myst::changeToCard(uint16 card, TransitionType transition) {
// Make sure the screen is updated
if (transition != kNoTransition) {
- if (!_gameState->_globals.transitions)
- transition = kTransitionCopy;
-
- _gfx->runTransition(transition, Common::Rect(544, 333), 10, 0);
+ if (_gameState->_globals.transitions) {
+ _gfx->runTransition(transition, Common::Rect(544, 333), 10, 0);
+ } else {
+ _gfx->copyBackBufferToScreen(Common::Rect(544, 333));
+ _needsUpdate = true;
+ }
}
// Make sure we have the right cursor showing
- _dragResource = 0;
- _hoverResource = 0;
+ _hoverResource = nullptr;
_curResource = -1;
checkCurrentResource();
@@ -671,13 +637,13 @@ void MohawkEngine_Myst::checkCurrentResource() {
// Tell previous resource the mouse is no longer hovering it
if (_hoverResource && !_hoverResource->contains(mouse)) {
_hoverResource->handleMouseLeave();
- _hoverResource = 0;
+ _hoverResource = nullptr;
}
for (uint16 i = 0; i < _resources.size(); i++)
if (_resources[i]->contains(mouse)) {
- if (_hoverResource != _resources[i] && _resources[i]->type == kMystHoverArea) {
- _hoverResource = static_cast<MystResourceType13 *>(_resources[i]);
+ if (_hoverResource != _resources[i] && _resources[i]->type == kMystAreaHover) {
+ _hoverResource = static_cast<MystAreaHover *>(_resources[i]);
_hoverResource->handleMouseEnter();
}
@@ -694,17 +660,17 @@ void MohawkEngine_Myst::checkCurrentResource() {
checkCursorHints();
}
-MystResource *MohawkEngine_Myst::updateCurrentResource() {
+MystArea *MohawkEngine_Myst::updateCurrentResource() {
checkCurrentResource();
if (_curResource >= 0)
return _resources[_curResource];
else
- return 0;
+ return nullptr;
}
void MohawkEngine_Myst::loadCard() {
- debugC(kDebugView, "Loading Card View:");
+ debugC(kDebugView, "Loading Card View: %d", _curCard);
Common::SeekableReadStream *viewStream = getResource(ID_VIEW, _curCard);
@@ -713,21 +679,23 @@ void MohawkEngine_Myst::loadCard() {
debugC(kDebugView, "Flags: 0x%04X", _view.flags);
// The Image Block (Reminiscent of Riven PLST resources)
- _view.conditionalImageCount = viewStream->readUint16LE();
- debugC(kDebugView, "Conditional Image Count: %d", _view.conditionalImageCount);
- if (_view.conditionalImageCount != 0) {
- _view.conditionalImages = new MystCondition[_view.conditionalImageCount];
- for (uint16 i = 0; i < _view.conditionalImageCount; i++) {
+ uint16 conditionalImageCount = viewStream->readUint16LE();
+ debugC(kDebugView, "Conditional Image Count: %d", conditionalImageCount);
+ if (conditionalImageCount != 0) {
+ for (uint16 i = 0; i < conditionalImageCount; i++) {
+ MystCondition conditionalImage;
+
debugC(kDebugView, "\tImage %d:", i);
- _view.conditionalImages[i].var = viewStream->readUint16LE();
- debugC(kDebugView, "\t\tVar: %d", _view.conditionalImages[i].var);
- _view.conditionalImages[i].numStates = viewStream->readUint16LE();
- debugC(kDebugView, "\t\tNumber of States: %d", _view.conditionalImages[i].numStates);
- _view.conditionalImages[i].values = new uint16[_view.conditionalImages[i].numStates];
- for (uint16 j = 0; j < _view.conditionalImages[i].numStates; j++) {
- _view.conditionalImages[i].values[j] = viewStream->readUint16LE();
- debugC(kDebugView, "\t\tState %d -> Value %d", j, _view.conditionalImages[i].values[j]);
+ conditionalImage.var = viewStream->readUint16LE();
+ debugC(kDebugView, "\t\tVar: %d", conditionalImage.var);
+ uint16 numStates = viewStream->readUint16LE();
+ debugC(kDebugView, "\t\tNumber of States: %d", numStates);
+ for (uint16 j = 0; j < numStates; j++) {
+ conditionalImage.values.push_back(viewStream->readUint16LE());
+ debugC(kDebugView, "\t\tState %d -> Value %d", j, conditionalImage.values[j]);
}
+
+ _view.conditionalImages.push_back(conditionalImage);
}
_view.mainImage = 0;
} else {
@@ -736,87 +704,58 @@ void MohawkEngine_Myst::loadCard() {
}
// The Sound Block (Reminiscent of Riven SLST resources)
- _view.sound = viewStream->readSint16LE();
- debugCN(kDebugView, "Sound Control: %d = ", _view.sound);
- if (_view.sound > 0) {
- debugC(kDebugView, "Play new Sound, change volume");
- debugC(kDebugView, "\tSound: %d", _view.sound);
- _view.soundVolume = viewStream->readUint16LE();
- debugC(kDebugView, "\tVolume: %d", _view.soundVolume);
- } else if (_view.sound == kMystSoundActionContinue)
- debugC(kDebugView, "Continue current sound");
- else if (_view.sound == kMystSoundActionChangeVolume) {
- debugC(kDebugView, "Continue current sound, change volume");
- _view.soundVolume = viewStream->readUint16LE();
- debugC(kDebugView, "\tVolume: %d", _view.soundVolume);
- } else if (_view.sound == kMystSoundActionStop) {
- debugC(kDebugView, "Stop sound");
- } else if (_view.sound == kMystSoundActionConditional) {
- debugC(kDebugView, "Conditional sound list");
- _view.soundVar = viewStream->readUint16LE();
- debugC(kDebugView, "\tVar: %d", _view.soundVar);
- _view.soundCount = viewStream->readUint16LE();
- debugC(kDebugView, "\tCount: %d", _view.soundCount);
- _view.soundList = new int16[_view.soundCount];
- _view.soundListVolume = new uint16[_view.soundCount];
-
- for (uint16 i = 0; i < _view.soundCount; i++) {
- _view.soundList[i] = viewStream->readSint16LE();
- debugC(kDebugView, "\t\tCondition %d: Action %d", i, _view.soundList[i]);
- if (_view.soundList[i] == kMystSoundActionChangeVolume || _view.soundList[i] >= 0) {
- _view.soundListVolume[i] = viewStream->readUint16LE();
- debugC(kDebugView, "\t\tCondition %d: Volume %d", i, _view.soundListVolume[i]);
- }
- }
- } else {
- debugC(kDebugView, "Unknown");
- warning("Unknown sound control value in card");
- }
+ _view.soundBlock = readSoundBlock(viewStream);
// Resources that scripts can call upon
- _view.scriptResCount = viewStream->readUint16LE();
- debugC(kDebugView, "Script Resource Count: %d", _view.scriptResCount);
- if (_view.scriptResCount != 0) {
- _view.scriptResources = new MystView::ScriptResource[_view.scriptResCount];
- for (uint16 i = 0; i < _view.scriptResCount; i++) {
- debugC(kDebugView, "\tResource %d:", i);
- _view.scriptResources[i].type = viewStream->readUint16LE();
- debugC(kDebugView, "\t\t Type: %d", _view.scriptResources[i].type);
-
- switch (_view.scriptResources[i].type) {
- case 1:
- debugC(kDebugView, "\t\t\t\t= Image");
- break;
- case 2:
- debugC(kDebugView, "\t\t\t\t= Sound");
- break;
- case 3:
- debugC(kDebugView, "\t\t\t\t= Resource List");
- break;
- default:
- debugC(kDebugView, "\t\t\t\t= Unknown");
- break;
- }
+ uint16 scriptResCount = viewStream->readUint16LE();
+ debugC(kDebugView, "Script Resource Count: %d", scriptResCount);
+ for (uint16 i = 0; i < scriptResCount; i++) {
+ MystView::ScriptResource scriptResource;
+
+ debugC(kDebugView, "\tResource %d:", i);
+ scriptResource.type = (MystView::ScriptResourceType) viewStream->readUint16LE();
+ debugC(kDebugView, "\t\t Type: %d", scriptResource.type);
+
+ switch (scriptResource.type) {
+ case MystView::kResourceImage:
+ debugC(kDebugView, "\t\t\t\t= Image");
+ break;
+ case MystView::kResourceSound:
+ debugC(kDebugView, "\t\t\t\t= Sound");
+ break;
+ case MystView::kResourceSwitch:
+ debugC(kDebugView, "\t\t\t\t= Resource Switch");
+ break;
+ case MystView::kResourceImageNoCache:
+ debugC(kDebugView, "\t\t\t\t= Image - Caching disabled");
+ break;
+ case MystView::kResourceSoundNoCache:
+ debugC(kDebugView, "\t\t\t\t= Sound - Caching disabled");
+ break;
+ default:
+ debugC(kDebugView, "\t\t\t\t= Unknown");
+ warning("Unknown script resource type '%d' in card '%d'", scriptResource.type, _curCard);
+ break;
+ }
- if (_view.scriptResources[i].type == 3) {
- _view.scriptResources[i].var = viewStream->readUint16LE();
- debugC(kDebugView, "\t\t Var: %d", _view.scriptResources[i].var);
- _view.scriptResources[i].count = viewStream->readUint16LE();
- debugC(kDebugView, "\t\t Resource List Count: %d", _view.scriptResources[i].count);
- _view.scriptResources[i].u0 = viewStream->readUint16LE();
- debugC(kDebugView, "\t\t u0: %d", _view.scriptResources[i].u0);
- _view.scriptResources[i].resource_list = new int16[_view.scriptResources[i].count];
-
- for (uint16 j = 0; j < _view.scriptResources[i].count; j++) {
- _view.scriptResources[i].resource_list[j] = viewStream->readSint16LE();
- debugC(kDebugView, "\t\t Resource List %d: %d", j, _view.scriptResources[i].resource_list[j]);
- }
- } else {
- _view.scriptResources[i].resource_list = NULL;
- _view.scriptResources[i].id = viewStream->readUint16LE();
- debugC(kDebugView, "\t\t Id: %d", _view.scriptResources[i].id);
+ if (scriptResource.type == MystView::kResourceSwitch) {
+ scriptResource.switchVar = viewStream->readUint16LE();
+ debugC(kDebugView, "\t\t Var: %d", scriptResource.switchVar);
+ uint16 count = viewStream->readUint16LE();
+ debugC(kDebugView, "\t\t Resource List Count: %d", count);
+ scriptResource.switchResourceType = (MystView::ScriptResourceType) viewStream->readUint16LE();
+ debugC(kDebugView, "\t\t u0: %d", scriptResource.switchResourceType);
+
+ for (uint16 j = 0; j < count; j++) {
+ scriptResource.switchResourceIds.push_back(viewStream->readSint16LE());
+ debugC(kDebugView, "\t\t Resource List %d: %d", j, scriptResource.switchResourceIds[j]);
}
+ } else {
+ scriptResource.id = viewStream->readUint16LE();
+ debugC(kDebugView, "\t\t Id: %d", scriptResource.id);
}
+
+ _view.scriptResources.push_back(scriptResource);
}
// Identifiers for other resources. 0 if non existent. There is always an RLST.
@@ -831,7 +770,6 @@ void MohawkEngine_Myst::loadCard() {
delete viewStream;
// Precache Card Resources
- // TODO: Deal with Mac ME External Picture File
uint32 cacheImageType;
if (getFeatures() & GF_ME)
cacheImageType = ID_PICT;
@@ -839,63 +777,58 @@ void MohawkEngine_Myst::loadCard() {
cacheImageType = ID_WDIB;
// Precache Image Block data
- if (_view.conditionalImageCount != 0) {
- for (uint16 i = 0; i < _view.conditionalImageCount; i++)
- for (uint16 j = 0; j < _view.conditionalImages[i].numStates; j++)
- cachePreload(cacheImageType, _view.conditionalImages[i].values[j]);
- } else
+ if (_view.conditionalImages.size() != 0) {
+ for (uint16 i = 0; i < _view.conditionalImages.size(); i++) {
+ uint16 value = _scriptParser->getVar(_view.conditionalImages[i].var);
+ cachePreload(cacheImageType, _view.conditionalImages[i].values[value]);
+ }
+ } else {
cachePreload(cacheImageType, _view.mainImage);
+ }
// Precache Sound Block data
- if (_view.sound > 0)
- cachePreload(ID_MSND, _view.sound);
- else if (_view.sound == kMystSoundActionConditional) {
- for (uint16 i = 0; i < _view.soundCount; i++) {
- if (_view.soundList[i] > 0)
- cachePreload(ID_MSND, _view.soundList[i]);
+ if (_view.soundBlock.sound > 0)
+ cachePreload(ID_MSND, _view.soundBlock.sound);
+ else if (_view.soundBlock.sound == kMystSoundActionConditional) {
+ uint16 value = _scriptParser->getVar(_view.soundBlock.soundVar);
+ if (_view.soundBlock.soundList[value].action > 0) {
+ cachePreload(ID_MSND, _view.soundBlock.soundList[value].action);
}
}
// Precache Script Resources
- if (_view.scriptResCount != 0) {
- for (uint16 i = 0; i < _view.scriptResCount; i++) {
- switch (_view.scriptResources[i].type) {
- case 1:
- cachePreload(cacheImageType, _view.scriptResources[i].id);
- break;
- case 2:
- cachePreload(ID_MSND, _view.scriptResources[i].id);
- break;
- case 3:
- warning("TODO: Precaching of Script Resource List not supported");
- break;
- default:
- warning("Unknown Resource in Script Resource List Precaching");
- break;
- }
+ for (uint16 i = 0; i < _view.scriptResources.size(); i++) {
+ MystView::ScriptResourceType type;
+ int16 id;
+ if (_view.scriptResources[i].type == MystView::kResourceSwitch) {
+ type = _view.scriptResources[i].switchResourceType;
+ uint16 value = _scriptParser->getVar(_view.scriptResources[i].switchVar);
+ id = _view.scriptResources[i].switchResourceIds[value];
+ } else {
+ type = _view.scriptResources[i].type;
+ id = _view.scriptResources[i].id;
+ }
+
+ if (id < 0) continue;
+
+ switch (type) {
+ case MystView::kResourceImage:
+ cachePreload(cacheImageType, id);
+ break;
+ case MystView::kResourceSound:
+ cachePreload(ID_MSND, id);
+ break;
+ default:
+ // The other resource types should not be cached
+ break;
}
}
}
void MohawkEngine_Myst::unloadCard() {
- for (uint16 i = 0; i < _view.conditionalImageCount; i++)
- delete[] _view.conditionalImages[i].values;
-
- delete[] _view.conditionalImages;
- _view.conditionalImageCount = 0;
- _view.conditionalImages = NULL;
-
- delete[] _view.soundList;
- _view.soundList = NULL;
- delete[] _view.soundListVolume;
- _view.soundListVolume = NULL;
-
- for (uint16 i = 0; i < _view.scriptResCount; i++)
- delete[] _view.scriptResources[i].resource_list;
-
- delete[] _view.scriptResources;
- _view.scriptResources = NULL;
- _view.scriptResCount = 0;
+ _view.conditionalImages.clear();
+ _view.soundBlock.soundList.clear();
+ _view.scriptResources.clear();
}
void MohawkEngine_Myst::runInitScript() {
@@ -968,14 +901,12 @@ void MohawkEngine_Myst::loadHelp(uint16 id) {
debugC(kDebugHelp, "\thelpText: \"%s\"", helpText.c_str());
delete[] u0;
+
+ delete helpStream;
}
void MohawkEngine_Myst::loadCursorHints() {
- for (uint16 i = 0; i < _cursorHintCount; i++)
- delete[] _cursorHints[i].variableHint.values;
- _cursorHintCount = 0;
- delete[] _cursorHints;
- _cursorHints = NULL;
+ _cursorHints.clear();
if (!_view.hint) {
debugC(kDebugHint, "No HINT Present");
@@ -985,33 +916,33 @@ void MohawkEngine_Myst::loadCursorHints() {
debugC(kDebugHint, "Loading Cursor Hints:");
Common::SeekableReadStream *hintStream = getResource(ID_HINT, _curCard);
- _cursorHintCount = hintStream->readUint16LE();
- debugC(kDebugHint, "Cursor Hint Count: %d", _cursorHintCount);
- _cursorHints = new MystCursorHint[_cursorHintCount];
+ uint16 cursorHintCount = hintStream->readUint16LE();
+ debugC(kDebugHint, "Cursor Hint Count: %d", cursorHintCount);
+
+ for (uint16 i = 0; i < cursorHintCount; i++) {
+ MystCursorHint hint;
- for (uint16 i = 0; i < _cursorHintCount; i++) {
debugC(kDebugHint, "Cursor Hint %d:", i);
- _cursorHints[i].id = hintStream->readUint16LE();
- debugC(kDebugHint, "\tId: %d", _cursorHints[i].id);
- _cursorHints[i].cursor = hintStream->readSint16LE();
- debugC(kDebugHint, "\tCursor: %d", _cursorHints[i].cursor);
+ hint.id = hintStream->readUint16LE();
+ debugC(kDebugHint, "\tId: %d", hint.id);
+ hint.cursor = hintStream->readSint16LE();
+ debugC(kDebugHint, "\tCursor: %d", hint.cursor);
- if (_cursorHints[i].cursor == -1) {
+ if (hint.cursor == -1) {
debugC(kDebugHint, "\tConditional Cursor Hints:");
- _cursorHints[i].variableHint.var = hintStream->readUint16LE();
- debugC(kDebugHint, "\tVar: %d", _cursorHints[i].variableHint.var);
- _cursorHints[i].variableHint.numStates = hintStream->readUint16LE();
- debugC(kDebugHint, "\tNumber of States: %d", _cursorHints[i].variableHint.numStates);
- _cursorHints[i].variableHint.values = new uint16[_cursorHints[i].variableHint.numStates];
- for (uint16 j = 0; j < _cursorHints[i].variableHint.numStates; j++) {
- _cursorHints[i].variableHint.values[j] = hintStream->readUint16LE();
- debugC(kDebugHint, "\t\t State %d: Cursor %d", j, _cursorHints[i].variableHint.values[j]);
+ hint.variableHint.var = hintStream->readUint16LE();
+ debugC(kDebugHint, "\tVar: %d", hint.variableHint.var);
+ uint16 numStates = hintStream->readUint16LE();
+ debugC(kDebugHint, "\tNumber of States: %d", numStates);
+ for (uint16 j = 0; j < numStates; j++) {
+ hint.variableHint.values.push_back(hintStream->readUint16LE());
+ debugC(kDebugHint, "\t\t State %d: Cursor %d", j, hint.variableHint.values[j]);
}
} else {
- _cursorHints[i].variableHint.var = 0;
- _cursorHints[i].variableHint.numStates = 0;
- _cursorHints[i].variableHint.values = NULL;
+ hint.variableHint.var = 0;
}
+
+ _cursorHints.push_back(hint);
}
delete hintStream;
@@ -1033,12 +964,12 @@ void MohawkEngine_Myst::checkCursorHints() {
}
// Check all the cursor hints to see if we're in a hotspot that contains a hint.
- for (uint16 i = 0; i < _cursorHintCount; i++)
+ for (uint16 i = 0; i < _cursorHints.size(); i++)
if (_cursorHints[i].id == _curResource && _resources[_cursorHints[i].id]->isEnabled()) {
if (_cursorHints[i].cursor == -1) {
uint16 var_value = _scriptParser->getVar(_cursorHints[i].variableHint.var);
- if (var_value >= _cursorHints[i].variableHint.numStates)
+ if (var_value >= _cursorHints[i].variableHint.values.size())
warning("Variable %d Out of Range in variable HINT Resource %d", _cursorHints[i].variableHint.var, i);
else {
_currentCursor = _cursorHints[i].variableHint.values[var_value];
@@ -1076,50 +1007,50 @@ void MohawkEngine_Myst::drawResourceImages() {
_resources[i]->drawDataToScreen();
}
-void MohawkEngine_Myst::redrawResource(MystResourceType8 *resource, bool update) {
- resource->drawConditionalDataToScreen(_scriptParser->getVar(resource->getType8Var()), update);
+void MohawkEngine_Myst::redrawResource(MystAreaImageSwitch *resource, bool update) {
+ resource->drawConditionalDataToScreen(_scriptParser->getVar(resource->getImageSwitchVar()), update);
}
void MohawkEngine_Myst::redrawArea(uint16 var, bool update) {
for (uint16 i = 0; i < _resources.size(); i++)
- if (_resources[i]->type == kMystConditionalImage && _resources[i]->getType8Var() == var)
- redrawResource(static_cast<MystResourceType8 *>(_resources[i]), update);
+ if (_resources[i]->type == kMystAreaImageSwitch && _resources[i]->getImageSwitchVar() == var)
+ redrawResource(static_cast<MystAreaImageSwitch *>(_resources[i]), update);
}
-MystResource *MohawkEngine_Myst::loadResource(Common::SeekableReadStream *rlstStream, MystResource *parent) {
- MystResource *resource = 0;
+MystArea *MohawkEngine_Myst::loadResource(Common::SeekableReadStream *rlstStream, MystArea *parent) {
+ MystArea *resource = nullptr;
ResourceType type = static_cast<ResourceType>(rlstStream->readUint16LE());
debugC(kDebugResource, "\tType: %d", type);
- debugC(kDebugResource, "\tSub_Record: %d", (parent == NULL) ? 0 : 1);
+ debugC(kDebugResource, "\tSub_Record: %d", (parent == nullptr) ? 0 : 1);
switch (type) {
- case kMystAction:
- resource = new MystResourceType5(this, rlstStream, parent);
+ case kMystAreaAction:
+ resource = new MystAreaAction(this, rlstStream, parent);
break;
- case kMystVideo:
- resource = new MystResourceType6(this, rlstStream, parent);
+ case kMystAreaVideo:
+ resource = new MystAreaVideo(this, rlstStream, parent);
break;
- case kMystSwitch:
- resource = new MystResourceType7(this, rlstStream, parent);
+ case kMystAreaActionSwitch:
+ resource = new MystAreaActionSwitch(this, rlstStream, parent);
break;
- case kMystConditionalImage:
- resource = new MystResourceType8(this, rlstStream, parent);
+ case kMystAreaImageSwitch:
+ resource = new MystAreaImageSwitch(this, rlstStream, parent);
break;
- case kMystSlider:
- resource = new MystResourceType10(this, rlstStream, parent);
+ case kMystAreaSlider:
+ resource = new MystAreaSlider(this, rlstStream, parent);
break;
- case kMystDragArea:
- resource = new MystResourceType11(this, rlstStream, parent);
+ case kMystAreaDrag:
+ resource = new MystAreaDrag(this, rlstStream, parent);
break;
case kMystVideoInfo:
- resource = new MystResourceType12(this, rlstStream, parent);
+ resource = new MystVideoInfo(this, rlstStream, parent);
break;
- case kMystHoverArea:
- resource = new MystResourceType13(this, rlstStream, parent);
+ case kMystAreaHover:
+ resource = new MystAreaHover(this, rlstStream, parent);
break;
default:
- resource = new MystResource(this, rlstStream, parent);
+ resource = new MystArea(this, rlstStream, parent);
break;
}
@@ -1145,34 +1076,42 @@ void MohawkEngine_Myst::loadResources() {
for (uint16 i = 0; i < resourceCount; i++) {
debugC(kDebugResource, "Resource #%d:", i);
- _resources.push_back(loadResource(rlstStream, NULL));
+ _resources.push_back(loadResource(rlstStream, nullptr));
}
delete rlstStream;
}
Common::Error MohawkEngine_Myst::loadGameState(int slot) {
- if (_gameState->load(_gameState->generateSaveGameList()[slot]))
+ if (_gameState->load(MystGameState::generateSaveGameList()[slot]))
return Common::kNoError;
return Common::kUnknownError;
}
Common::Error MohawkEngine_Myst::saveGameState(int slot, const Common::String &desc) {
- Common::StringArray saveList = _gameState->generateSaveGameList();
+ Common::StringArray saveList = MystGameState::generateSaveGameList();
if ((uint)slot < saveList.size())
- _gameState->deleteSave(saveList[slot]);
+ MystGameState::deleteSave(saveList[slot]);
+
+ return _gameState->save(desc) ? Common::kNoError : Common::kUnknownError;
+}
- return _gameState->save(Common::String(desc)) ? Common::kNoError : Common::kUnknownError;
+bool MohawkEngine_Myst::hasGameSaveSupport() const {
+ return !(getFeatures() & GF_DEMO) && getGameType() != GType_MAKINGOF;
}
bool MohawkEngine_Myst::canLoadGameStateCurrently() {
// No loading in the demo/makingof
- return !(getFeatures() & GF_DEMO) && getGameType() != GType_MAKINGOF;
+ return _canSafelySaveLoad && hasGameSaveSupport();
}
bool MohawkEngine_Myst::canSaveGameStateCurrently() {
+ if (!_canSafelySaveLoad) {
+ return false;
+ }
+
// There's a limited number of stacks the game can save in
switch (_curStack) {
case kChannelwoodStack:
@@ -1225,4 +1164,82 @@ void MohawkEngine_Myst::dropPage() {
checkCursorHints();
}
+MystSoundBlock MohawkEngine_Myst::readSoundBlock(Common::ReadStream *stream) const {
+ MystSoundBlock soundBlock;
+ soundBlock.sound = stream->readSint16LE();
+ debugCN(kDebugView, "Sound Control: %d = ", soundBlock.sound);
+
+ if (soundBlock.sound > 0) {
+ debugC(kDebugView, "Play new Sound, change volume");
+ debugC(kDebugView, "\tSound: %d", soundBlock.sound);
+ soundBlock.soundVolume = stream->readUint16LE();
+ debugC(kDebugView, "\tVolume: %d", soundBlock.soundVolume);
+ } else if (soundBlock.sound == kMystSoundActionContinue)
+ debugC(kDebugView, "Continue current sound");
+ else if (soundBlock.sound == kMystSoundActionChangeVolume) {
+ debugC(kDebugView, "Continue current sound, change volume");
+ soundBlock.soundVolume = stream->readUint16LE();
+ debugC(kDebugView, "\tVolume: %d", soundBlock.soundVolume);
+ } else if (soundBlock.sound == kMystSoundActionStop) {
+ debugC(kDebugView, "Stop sound");
+ } else if (soundBlock.sound == kMystSoundActionConditional) {
+ debugC(kDebugView, "Conditional sound list");
+ soundBlock.soundVar = stream->readUint16LE();
+ debugC(kDebugView, "\tVar: %d", soundBlock.soundVar);
+ uint16 soundCount = stream->readUint16LE();
+ debugC(kDebugView, "\tCount: %d", soundCount);
+
+ for (uint16 i = 0; i < soundCount; i++) {
+ MystSoundBlock::SoundItem sound;
+
+ sound.action = stream->readSint16LE();
+ debugC(kDebugView, "\t\tCondition %d: Action %d", i, sound.action);
+ if (sound.action == kMystSoundActionChangeVolume || sound.action >= 0) {
+ sound.volume = stream->readUint16LE();
+ debugC(kDebugView, "\t\tCondition %d: Volume %d", i, sound.volume);
+ }
+
+ soundBlock.soundList.push_back(sound);
+ }
+ } else {
+ debugC(kDebugView, "Unknown");
+ warning("Unknown sound control value '%d' in card '%d'", soundBlock.sound, _curCard);
+ }
+
+ return soundBlock;
+}
+
+void MohawkEngine_Myst::applySoundBlock(const MystSoundBlock &block) {
+ int16 soundAction = 0;
+ uint16 soundActionVolume = 0;
+
+ if (block.sound == kMystSoundActionConditional) {
+ uint16 soundVarValue = _scriptParser->getVar(block.soundVar);
+ if (soundVarValue >= block.soundList.size())
+ warning("Conditional sound variable outside range");
+ else {
+ soundAction = block.soundList[soundVarValue].action;
+ soundActionVolume = block.soundList[soundVarValue].volume;
+ }
+ } else {
+ soundAction = block.sound;
+ soundActionVolume = block.soundVolume;
+ }
+
+ if (soundAction == kMystSoundActionContinue)
+ debug(2, "Continuing with current sound");
+ else if (soundAction == kMystSoundActionChangeVolume) {
+ debug(2, "Continuing with current sound, changing volume");
+ _sound->changeBackgroundVolumeMyst(soundActionVolume);
+ } else if (soundAction == kMystSoundActionStop) {
+ debug(2, "Stopping sound");
+ _sound->stopBackgroundMyst();
+ } else if (soundAction > 0) {
+ debug(2, "Playing new sound %d", soundAction);
+ _sound->replaceBackgroundMyst(soundAction, soundActionVolume);
+ } else {
+ error("Unknown sound action %d", soundAction);
+ }
+}
+
} // End of namespace Mohawk
diff --git a/engines/mohawk/myst.h b/engines/mohawk/myst.h
index 4d86642652..0b249e5499 100644
--- a/engines/mohawk/myst.h
+++ b/engines/mohawk/myst.h
@@ -28,10 +28,9 @@
#include "mohawk/resource_cache.h"
#include "mohawk/myst_scripts.h"
+#include "common/events.h"
#include "common/random.h"
-#include "gui/saveload.h"
-
namespace Mohawk {
class MohawkEngine_Myst;
@@ -41,9 +40,9 @@ class MystScriptParser;
class MystConsole;
class MystGameState;
class MystOptionsDialog;
-class MystResource;
-class MystResourceType8;
-class MystResourceType13;
+class MystArea;
+class MystAreaImageSwitch;
+class MystAreaHover;
// Engine Debug Flags
enum {
@@ -96,8 +95,19 @@ const uint16 kMasterpieceOnly = 0xFFFF;
struct MystCondition {
uint16 var;
- uint16 numStates;
- uint16 *values;
+ Common::Array<uint16> values;
+};
+
+struct MystSoundBlock {
+ struct SoundItem {
+ int16 action;
+ uint16 volume;
+ };
+
+ int16 sound;
+ uint16 soundVolume;
+ uint16 soundVar;
+ Common::Array<SoundItem> soundList;
};
// View Sound Action Type
@@ -118,29 +128,29 @@ struct MystView {
uint16 flags;
// Image Data
- uint16 conditionalImageCount;
- MystCondition *conditionalImages;
+ Common::Array<MystCondition> conditionalImages;
uint16 mainImage;
// Sound Data
- int16 sound;
- uint16 soundVolume;
- uint16 soundVar;
- uint16 soundCount;
- int16 *soundList;
- uint16 *soundListVolume;
+ MystSoundBlock soundBlock;
// Script Resources
- uint16 scriptResCount;
+ enum ScriptResourceType {
+ kResourceImage = 1,
+ kResourceSound = 2,
+ kResourceSwitch = 3,
+ kResourceImageNoCache = 4,
+ kResourceSoundNoCache = 5
+ };
+
struct ScriptResource {
- uint16 type;
- uint16 id; // Not used by type 3
- // TODO: Type 3 has more. Maybe use a union?
- uint16 var; // Used by type 3 only
- uint16 count; // Used by type 3 only
- uint16 u0; // Used by type 3 only
- int16 *resource_list; // Used by type 3 only
- } *scriptResources;
+ ScriptResourceType type;
+ uint16 id;
+ uint16 switchVar;
+ ScriptResourceType switchResourceType;
+ Common::Array<int16> switchResourceIds;
+ };
+ Common::Array<ScriptResource> scriptResources;
// Resource ID's
uint16 rlst;
@@ -158,18 +168,17 @@ struct MystCursorHint {
class MohawkEngine_Myst : public MohawkEngine {
protected:
- Common::Error run();
+ Common::Error run() override;
public:
MohawkEngine_Myst(OSystem *syst, const MohawkGameDescription *gamedesc);
virtual ~MohawkEngine_Myst();
- Common::SeekableReadStream *getResource(uint32 tag, uint16 id);
+ Common::SeekableReadStream *getResource(uint32 tag, uint16 id) override;
+ Common::Array<uint16> getResourceIDList(uint32 type) const;
Common::String wrapMovieFilename(const Common::String &movieName, uint16 stack);
- void reloadSaveList();
-
void changeToStack(uint16 stack, uint16 card, uint16 linkSrcSound, uint16 linkDstSound);
void changeToCard(uint16 card, TransitionType transition);
uint16 getCurCard() { return _curCard; }
@@ -177,46 +186,50 @@ public:
void setMainCursor(uint16 cursor);
uint16 getMainCursor() { return _mainCursor; }
void checkCursorHints();
- MystResource *updateCurrentResource();
+ MystArea *updateCurrentResource();
bool skippableWait(uint32 duration);
- bool _tweaksEnabled;
+ MystSoundBlock readSoundBlock(Common::ReadStream *stream) const;
+ void applySoundBlock(const MystSoundBlock &block);
+
bool _needsUpdate;
bool _needsPageDrop;
bool _needsShowMap;
bool _needsShowDemoMenu;
+ bool _needsShowCredits;
+
+ bool _showResourceRects;
- MystView _view;
MystGraphics *_gfx;
MystGameState *_gameState;
MystScriptParser *_scriptParser;
- Common::Array<MystResource *> _resources;
- MystResource *_dragResource;
+ Common::Array<MystArea *> _resources;
Common::RandomSource *_rnd;
- bool _showResourceRects;
- MystResource *loadResource(Common::SeekableReadStream *rlstStream, MystResource *parent);
+ MystArea *loadResource(Common::SeekableReadStream *rlstStream, MystArea *parent);
void setResourceEnabled(uint16 resourceId, bool enable);
void redrawArea(uint16 var, bool update = true);
- void redrawResource(MystResourceType8 *resource, bool update = true);
+ void redrawResource(MystAreaImageSwitch *resource, bool update = true);
void drawResourceImages();
void drawCardBackground();
uint16 getCardBackgroundId();
+ template<class T>
+ T *getViewResource(uint index);
+
void setCacheState(bool state) { _cache.enabled = state; }
bool getCacheState() { return _cache.enabled; }
- GUI::Debugger *getDebugger() { return _console; }
+ GUI::Debugger *getDebugger() override { return _console; }
- bool canLoadGameStateCurrently();
- bool canSaveGameStateCurrently();
- Common::Error loadGameState(int slot);
- Common::Error saveGameState(int slot, const Common::String &desc);
- bool hasFeature(EngineFeature f) const;
+ bool canLoadGameStateCurrently() override;
+ bool canSaveGameStateCurrently() override;
+ Common::Error loadGameState(int slot) override;
+ Common::Error saveGameState(int slot, const Common::String &desc) override;
+ bool hasFeature(EngineFeature f) const override;
private:
MystConsole *_console;
- GUI::SaveLoadChooser *_loadDialog;
MystOptionsDialog *_optionsDialog;
MystScriptParser *_prevStack;
ResourceCache _cache;
@@ -224,9 +237,18 @@ private:
uint16 _curStack;
uint16 _curCard;
+ MystView _view;
bool _runExitScript;
+ /**
+ * Saving / Loading is only allowed from the main event loop
+ */
+ bool _canSafelySaveLoad;
+ bool hasGameSaveSupport() const;
+
+ bool pollEvent(Common::Event &event);
+
void dropPage();
void loadCard();
@@ -240,15 +262,25 @@ private:
void drawResourceRects();
void checkCurrentResource();
int16 _curResource;
- MystResourceType13 *_hoverResource;
+ MystAreaHover *_hoverResource;
- uint16 _cursorHintCount;
- MystCursorHint *_cursorHints;
+ Common::Array<MystCursorHint> _cursorHints;
void loadCursorHints();
uint16 _currentCursor;
uint16 _mainCursor; // Also defines the current page being held (white, blue, red, or none)
};
+template<class T>
+T *MohawkEngine_Myst::getViewResource(uint index) {
+ T *resource = dynamic_cast<T *>(_resources[index]);
+
+ if (!resource) {
+ error("View resource '%d' has unexpected type", index);
+ }
+
+ return resource;
+}
+
} // End of namespace Mohawk
#endif
diff --git a/engines/mohawk/myst_areas.cpp b/engines/mohawk/myst_areas.cpp
index 7a9596d8e0..4b9cf546fa 100644
--- a/engines/mohawk/myst_areas.cpp
+++ b/engines/mohawk/myst_areas.cpp
@@ -32,11 +32,11 @@
namespace Mohawk {
-MystResource::MystResource(MohawkEngine_Myst *vm, Common::SeekableReadStream *rlstStream, MystResource *parent) {
+MystArea::MystArea(MohawkEngine_Myst *vm, Common::SeekableReadStream *rlstStream, MystArea *parent) {
_vm = vm;
_parent = parent;
- if (parent == NULL) {
+ if (parent == nullptr) {
_flags = rlstStream->readUint16LE();
_rect.left = rlstStream->readSint16LE();
_rect.top = rlstStream->readSint16LE();
@@ -66,10 +66,10 @@ MystResource::MystResource(MohawkEngine_Myst *vm, Common::SeekableReadStream *rl
debugC(kDebugResource, "\tdest: %d", _dest);
}
-MystResource::~MystResource() {
+MystArea::~MystArea() {
}
-void MystResource::handleMouseUp() {
+void MystArea::handleMouseUp() {
if (_dest == 0) {
warning("Movement type resource with null destination at position (%d, %d), (%d, %d)", _rect.left, _rect.top, _rect.right, _rect.bottom);
return;
@@ -78,13 +78,13 @@ void MystResource::handleMouseUp() {
uint16 opcode;
switch (type) {
- case kMystForwardArea:
+ case kMystAreaForward:
opcode = 6;
break;
- case kMystLeftArea:
+ case kMystAreaLeft:
opcode = 8;
break;
- case kMystRightArea:
+ case kMystAreaRight:
opcode = 7;
break;
default:
@@ -96,27 +96,27 @@ void MystResource::handleMouseUp() {
_vm->_scriptParser->runOpcode(opcode, 0);
}
-bool MystResource::canBecomeActive() {
+bool MystArea::canBecomeActive() {
return !unreachableZipDest() && (isEnabled() || (_flags & kMystUnknownFlag));
}
-bool MystResource::unreachableZipDest() {
+bool MystArea::unreachableZipDest() {
return (_flags & kMystZipModeEnableFlag)
&& !_vm->_gameState->isReachableZipDest(_vm->getCurStack() , _dest);
}
-bool MystResource::isEnabled() {
+bool MystArea::isEnabled() {
return _flags & kMystHotspotEnableFlag;
}
-void MystResource::setEnabled(bool enabled) {
+void MystArea::setEnabled(bool enabled) {
if (enabled)
_flags |= kMystHotspotEnableFlag;
else
_flags &= ~kMystHotspotEnableFlag;
}
-const Common::String MystResource::describe() {
+const Common::String MystArea::describe() {
Common::String desc = Common::String::format("type: %2d rect: (%3d %3d %3d %3d)",
type, _rect.left, _rect.top, _rect.width(), _rect.height());
@@ -126,7 +126,7 @@ const Common::String MystResource::describe() {
return desc;
}
-void MystResource::drawBoundingRect() {
+void MystArea::drawBoundingRect() {
if (_rect.isValidRect()) {
if (!canBecomeActive())
_vm->_gfx->drawRect(_rect, kRectUnreachable);
@@ -137,18 +137,19 @@ void MystResource::drawBoundingRect() {
}
}
-MystResourceType5::MystResourceType5(MohawkEngine_Myst *vm, Common::SeekableReadStream *rlstStream, MystResource *parent) : MystResource(vm, rlstStream, parent) {
+MystAreaAction::MystAreaAction(MohawkEngine_Myst *vm, Common::SeekableReadStream *rlstStream, MystArea *parent) :
+ MystArea(vm, rlstStream, parent) {
debugC(kDebugResource, "\tResource Type 5 Script:");
_script = vm->_scriptParser->readScript(rlstStream, kMystScriptNormal);
}
-void MystResourceType5::handleMouseUp() {
+void MystAreaAction::handleMouseUp() {
_vm->_scriptParser->runScript(_script, this);
}
-const Common::String MystResourceType5::describe() {
- Common::String desc = MystResource::describe();
+const Common::String MystAreaAction::describe() {
+ Common::String desc = MystArea::describe();
if (_script->size() != 0) {
desc += " ops:";
@@ -161,7 +162,7 @@ const Common::String MystResourceType5::describe() {
}
// In Myst/Making of Myst, the paths are hardcoded ala Windows style without extension. Convert them.
-Common::String MystResourceType6::convertMystVideoName(Common::String name) {
+Common::String MystAreaVideo::convertMystVideoName(Common::String name) {
Common::String temp;
for (uint32 i = 1; i < name.size(); i++) {
@@ -174,7 +175,8 @@ Common::String MystResourceType6::convertMystVideoName(Common::String name) {
return temp + ".mov";
}
-MystResourceType6::MystResourceType6(MohawkEngine_Myst *vm, Common::SeekableReadStream *rlstStream, MystResource *parent) : MystResourceType5(vm, rlstStream, parent) {
+MystAreaVideo::MystAreaVideo(MohawkEngine_Myst *vm, Common::SeekableReadStream *rlstStream, MystArea *parent) :
+ MystAreaAction(vm, rlstStream, parent) {
char c = 0;
do {
@@ -197,16 +199,7 @@ MystResourceType6::MystResourceType6(MohawkEngine_Myst *vm, Common::SeekableRead
_direction = rlstStream->readSint16LE();
_playBlocking = rlstStream->readUint16LE();
_loop = rlstStream->readUint16LE();
- _u3 = rlstStream->readUint16LE();
-
- // TODO: Out of bound values should clip the movie
- if (_left < 0)
- _left = 0;
- if (_top < 0)
- _top = 0;
-
- if (_u3 != 0)
- warning("Type 6 _u3 != 0");
+ _playRate = rlstStream->readUint16LE();
debugC(kDebugResource, "\tvideoFile: \"%s\"", _videoFile.c_str());
debugC(kDebugResource, "\tleft: %d", _left);
@@ -215,15 +208,15 @@ MystResourceType6::MystResourceType6(MohawkEngine_Myst *vm, Common::SeekableRead
debugC(kDebugResource, "\tdirection: %d", _direction);
debugC(kDebugResource, "\tplayBlocking: %d", _playBlocking);
debugC(kDebugResource, "\tplayOnCardChange: %d", _playOnCardChange);
- debugC(kDebugResource, "\tu3: %d", _u3);
+ debugC(kDebugResource, "\tplayRate: %d", _playRate);
}
-VideoHandle MystResourceType6::playMovie() {
+VideoHandle MystAreaVideo::playMovie() {
// Check if the video is already running
VideoHandle handle = _vm->_video->findVideoHandle(_videoFile);
// If the video is not running, play it
- if (!handle || handle->endOfVideo()) {
+ if (!handle) {
handle = _vm->_video->playMovie(_videoFile);
if (!handle)
error("Failed to open '%s'", _videoFile.c_str());
@@ -231,13 +224,23 @@ VideoHandle MystResourceType6::playMovie() {
handle->moveTo(_left, _top);
handle->setLooping(_loop != 0);
+ Common::Rational rate;
+ if (_playRate != 0) {
+ rate = Common::Rational(_playRate, 100);
+ } else {
+ rate = 1;
+ }
+
if (_direction == -1) {
+ rate = -rate;
handle->seek(handle->getDuration());
- handle->setRate(-1);
}
+
+ handle->setRate(rate);
} else {
// Resume the video
handle->pause(false);
+ handle->start();
}
if (_playBlocking) {
@@ -248,186 +251,156 @@ VideoHandle MystResourceType6::playMovie() {
return handle;
}
-void MystResourceType6::handleCardChange() {
+VideoHandle MystAreaVideo::getMovieHandle() {
+ // If the video is already in the manager, just return the handle
+ VideoHandle handle = _vm->_video->findVideoHandle(_videoFile);
+ if (!handle) {
+ // If the video has not been loaded yet, do it but don't start playing it
+ handle = _vm->_video->playMovie(_videoFile);
+ if (!handle)
+ error("Failed to open '%s'", _videoFile.c_str());
+ handle->stop();
+ }
+
+ return handle;
+}
+
+void MystAreaVideo::handleCardChange() {
if (_playOnCardChange)
playMovie();
}
-bool MystResourceType6::isPlaying() {
+bool MystAreaVideo::isPlaying() {
VideoHandle handle = _vm->_video->findVideoHandle(_videoFile);
return handle && !handle->endOfVideo();
}
-void MystResourceType6::pauseMovie(bool pause) {
+void MystAreaVideo::pauseMovie(bool pause) {
VideoHandle handle = _vm->_video->findVideoHandle(_videoFile);
if (handle && !handle->endOfVideo())
handle->pause(pause);
}
-MystResourceType7::MystResourceType7(MohawkEngine_Myst *vm, Common::SeekableReadStream *rlstStream, MystResource *parent) : MystResource(vm, rlstStream, parent) {
- _var7 = rlstStream->readUint16LE();
- _numSubResources = rlstStream->readUint16LE();
- debugC(kDebugResource, "\tvar7: %d", _var7);
- debugC(kDebugResource, "\tnumSubResources: %d", _numSubResources);
+MystAreaActionSwitch::MystAreaActionSwitch(MohawkEngine_Myst *vm, Common::SeekableReadStream *rlstStream, MystArea *parent) :
+ MystArea(vm, rlstStream, parent) {
+ _actionSwitchVar = rlstStream->readUint16LE();
+ uint16 numSubResources = rlstStream->readUint16LE();
+ debugC(kDebugResource, "\tactionSwitchVar: %d", _actionSwitchVar);
+ debugC(kDebugResource, "\tnumSubResources: %d", numSubResources);
- for (uint16 i = 0; i < _numSubResources; i++)
+ for (uint16 i = 0; i < numSubResources; i++)
_subResources.push_back(vm->loadResource(rlstStream, this));
}
-MystResourceType7::~MystResourceType7() {
+MystAreaActionSwitch::~MystAreaActionSwitch() {
for (uint32 i = 0; i < _subResources.size(); i++)
delete _subResources[i];
_subResources.clear();
}
-// TODO: All these functions to switch subresource are very similar.
-// Find way to share code (function pointer pass?)
-void MystResourceType7::drawDataToScreen() {
- if (_var7 == 0xFFFF) {
- if (_numSubResources == 1)
- _subResources[0]->drawDataToScreen();
- else if (_numSubResources != 0)
- warning("Type 7 Resource with _numSubResources of %d, but no control variable", _numSubResources);
+void MystAreaActionSwitch::doSwitch(AreaHandler handler) {
+ if (_actionSwitchVar == 0xFFFF) {
+ if (_subResources.size() == 1)
+ (_subResources[0]->*handler)();
+ else if (_subResources.size() != 0)
+ warning("Action switch resource with _numSubResources of %d, but no control variable", _subResources.size());
} else {
- uint16 varValue = _vm->_scriptParser->getVar(_var7);
+ uint16 varValue = _vm->_scriptParser->getVar(_actionSwitchVar);
- if (_numSubResources == 1 && varValue != 0)
- _subResources[0]->drawDataToScreen();
- else if (_numSubResources != 0) {
- if (varValue < _numSubResources)
- _subResources[varValue]->drawDataToScreen();
+ if (_subResources.size() == 1 && varValue != 0)
+ (_subResources[0]->*handler)();
+ else if (_subResources.size() != 0) {
+ if (varValue < _subResources.size())
+ (_subResources[varValue]->*handler)();
else
- warning("Type 7 Resource Var %d: %d exceeds number of sub resources %d", _var7, varValue, _numSubResources);
+ warning("Action switch resource Var %d: %d exceeds number of sub resources %d", _actionSwitchVar, varValue, _subResources.size());
}
}
}
-void MystResourceType7::handleCardChange() {
- if (_var7 == 0xFFFF) {
- if (_numSubResources == 1)
- _subResources[0]->handleCardChange();
- else if (_numSubResources != 0)
- warning("Type 7 Resource with _numSubResources of %d, but no control variable", _numSubResources);
- } else {
- uint16 varValue = _vm->_scriptParser->getVar(_var7);
-
- if (_numSubResources == 1 && varValue != 0)
- _subResources[0]->handleCardChange();
- else if (_numSubResources != 0) {
- if (varValue < _numSubResources)
- _subResources[varValue]->handleCardChange();
- else
- warning("Type 7 Resource Var %d: %d exceeds number of sub resources %d", _var7, varValue, _numSubResources);
- }
- }
+void MystAreaActionSwitch::drawDataToScreen() {
+ doSwitch(&MystArea::drawDataToScreen);
}
-void MystResourceType7::handleMouseUp() {
- if (_var7 == 0xFFFF) {
- if (_numSubResources == 1)
- _subResources[0]->handleMouseUp();
- else if (_numSubResources != 0)
- warning("Type 7 Resource with _numSubResources of %d, but no control variable", _numSubResources);
- } else {
- uint16 varValue = _vm->_scriptParser->getVar(_var7);
-
- if (_numSubResources == 1 && varValue != 0)
- _subResources[0]->handleMouseUp();
- else if (_numSubResources != 0) {
- if (varValue < _numSubResources)
- _subResources[varValue]->handleMouseUp();
- else
- warning("Type 7 Resource Var %d: %d exceeds number of sub resources %d", _var7, varValue, _numSubResources);
- }
- }
+void MystAreaActionSwitch::handleCardChange() {
+ doSwitch(&MystArea::handleCardChange);
}
-void MystResourceType7::handleMouseDown() {
- if (_var7 == 0xFFFF) {
- if (_numSubResources == 1)
- _subResources[0]->handleMouseDown();
- else if (_numSubResources != 0)
- warning("Type 7 Resource with _numSubResources of %d, but no control variable", _numSubResources);
- } else {
- uint16 varValue = _vm->_scriptParser->getVar(_var7);
-
- if (_numSubResources == 1 && varValue != 0)
- _subResources[0]->handleMouseDown();
- else if (_numSubResources != 0) {
- if (varValue < _numSubResources)
- _subResources[varValue]->handleMouseDown();
- else
- warning("Type 7 Resource Var %d: %d exceeds number of sub resources %d", _var7, varValue, _numSubResources);
- }
- }
+void MystAreaActionSwitch::handleMouseUp() {
+ doSwitch(&MystArea::handleMouseUp);
}
-MystResourceType8::MystResourceType8(MohawkEngine_Myst *vm, Common::SeekableReadStream *rlstStream, MystResource *parent) : MystResourceType7(vm, rlstStream, parent) {
- _var8 = rlstStream->readUint16LE();
- _numSubImages = rlstStream->readUint16LE();
- debugC(kDebugResource, "\tvar8: %d", _var8);
- debugC(kDebugResource, "\tnumSubImages: %d", _numSubImages);
+void MystAreaActionSwitch::handleMouseDown() {
+ doSwitch(&MystArea::handleMouseDown);
+}
- _subImages = new MystResourceType8::SubImage[_numSubImages];
+MystAreaImageSwitch::MystAreaImageSwitch(MohawkEngine_Myst *vm, Common::SeekableReadStream *rlstStream, MystArea *parent) :
+ MystAreaActionSwitch(vm, rlstStream, parent) {
+ _imageSwitchVar = rlstStream->readUint16LE();
+ uint16 numSubImages = rlstStream->readUint16LE();
+ debugC(kDebugResource, "\tvar8: %d", _imageSwitchVar);
+ debugC(kDebugResource, "\tnumSubImages: %d", numSubImages);
- for (uint16 i = 0; i < _numSubImages; i++) {
+ for (uint16 i = 0; i < numSubImages; i++) {
debugC(kDebugResource, "\tSubimage %d:", i);
- _subImages[i].wdib = rlstStream->readUint16LE();
- _subImages[i].rect.left = rlstStream->readSint16LE();
+ SubImage subImage;
+ subImage.wdib = rlstStream->readUint16LE();
+ subImage.rect.left = rlstStream->readSint16LE();
- if (_subImages[i].rect.left != -1) {
- _subImages[i].rect.top = rlstStream->readSint16LE();
- _subImages[i].rect.right = rlstStream->readSint16LE();
- _subImages[i].rect.bottom = rlstStream->readSint16LE();
+ if (subImage.rect.left != -1) {
+ subImage.rect.top = rlstStream->readSint16LE();
+ subImage.rect.right = rlstStream->readSint16LE();
+ subImage.rect.bottom = rlstStream->readSint16LE();
} else {
// Use the hotspot rect as the source rect since the subimage is fullscreen
// Convert to bitmap coordinates (upside down)
- _subImages[i].rect.left = _rect.left;
- _subImages[i].rect.top = 333 - _rect.bottom;
- _subImages[i].rect.right = _rect.right;
- _subImages[i].rect.bottom = 333 - _rect.top;
+ subImage.rect.left = _rect.left;
+ subImage.rect.top = 333 - _rect.bottom;
+ subImage.rect.right = _rect.right;
+ subImage.rect.bottom = 333 - _rect.top;
}
- debugC(kDebugResource, "\twdib: %d", _subImages[i].wdib);
- debugC(kDebugResource, "\tleft: %d", _subImages[i].rect.left);
- debugC(kDebugResource, "\ttop: %d", _subImages[i].rect.top);
- debugC(kDebugResource, "\tright: %d", _subImages[i].rect.right);
- debugC(kDebugResource, "\tbottom: %d", _subImages[i].rect.bottom);
+ debugC(kDebugResource, "\twdib: %d", subImage.wdib);
+ debugC(kDebugResource, "\tleft: %d", subImage.rect.left);
+ debugC(kDebugResource, "\ttop: %d", subImage.rect.top);
+ debugC(kDebugResource, "\tright: %d", subImage.rect.right);
+ debugC(kDebugResource, "\tbottom: %d", subImage.rect.bottom);
+
+ _subImages.push_back(subImage);
}
}
-MystResourceType8::~MystResourceType8() {
- delete[] _subImages;
+MystAreaImageSwitch::~MystAreaImageSwitch() {
}
-void MystResourceType8::drawDataToScreen() {
- // Need to call overidden Type 7 function to ensure
+void MystAreaImageSwitch::drawDataToScreen() {
+ // Need to call overridden function to ensure
// switch section is processed correctly.
- MystResourceType7::drawDataToScreen();
+ MystAreaActionSwitch::drawDataToScreen();
bool drawSubImage = false;
int16 subImageId = 0;
- if (_var8 == 0xFFFF) {
- if (_numSubImages == 1) {
+ if (_imageSwitchVar == 0xFFFF) {
+ if (_subImages.size() == 1) {
subImageId = 0;
drawSubImage = true;
- } else if (_numSubImages != 0)
- warning("Type 8 Resource with _numSubImages of %d, but no control variable", _numSubImages);
+ } else if (_subImages.size() != 0)
+ warning("Image Switch resource with _numSubImages of %d, but no control variable", _subImages.size());
} else {
- uint16 varValue = _vm->_scriptParser->getVar(_var8);
+ uint16 varValue = _vm->_scriptParser->getVar(_imageSwitchVar);
- if (_numSubImages == 1 && varValue != 0) {
+ if (_subImages.size() == 1 && varValue != 0) {
subImageId = 0;
drawSubImage = true;
- } else if (_numSubImages != 0) {
- if (varValue < _numSubImages) {
+ } else if (_subImages.size() != 0) {
+ if (varValue < _subImages.size()) {
subImageId = varValue;
drawSubImage = true;
} else
- warning("Type 8 Image Var %d: %d exceeds number of subImages %d", _var8, varValue, _numSubImages);
+ warning("Image Switch Var %d: %d exceeds number of subImages %d", _imageSwitchVar, varValue, _subImages.size());
}
}
@@ -442,20 +415,21 @@ void MystResourceType8::drawDataToScreen() {
}
}
-void MystResourceType8::drawConditionalDataToScreen(uint16 state, bool update) {
+//TODO: Merge with the method above?
+void MystAreaImageSwitch::drawConditionalDataToScreen(uint16 state, bool update) {
bool drawSubImage = false;
int16 subImageId = 0;
- if (_numSubImages == 1 && state != 0) {
+ if (_subImages.size() == 1 && state != 0) {
subImageId = 0;
drawSubImage = true;
- } else if (_numSubImages != 0) {
- if (state < _numSubImages) {
+ } else if (_subImages.size() != 0) {
+ if (state < _subImages.size()) {
subImageId = state;
drawSubImage = true;
} else
- warning("Type 8 Image Var %d: %d exceeds number of subImages %d", _var8, state, _numSubImages);
+ warning("Image Switch Var %d: %d exceeds number of subImages %d", _imageSwitchVar, state, _subImages.size());
}
@@ -476,18 +450,26 @@ void MystResourceType8::drawConditionalDataToScreen(uint16 state, bool update) {
}
}
-uint16 MystResourceType8::getType8Var() {
- return _var8;
+uint16 MystAreaImageSwitch::getImageSwitchVar() {
+ return _imageSwitchVar;
+}
+
+MystAreaImageSwitch::SubImage MystAreaImageSwitch::getSubImage(uint index) const {
+ return _subImages[index];
}
-const Common::String MystResourceType8::describe() {
+void MystAreaImageSwitch::setSubImageRect(uint index, const Common::Rect &rect) {
+ _subImages[index].rect = rect;
+}
+
+const Common::String MystAreaImageSwitch::describe() {
Common::String desc = Common::String::format("%s var: %2d",
- MystResourceType7::describe().c_str(), _var8);
+ MystAreaActionSwitch::describe().c_str(), _imageSwitchVar);
- if (_numSubImages > 0) {
+ if (_subImages.size() > 0) {
desc += " subImgs:";
- for (uint i = 0; i < _numSubImages; i++)
+ for (uint i = 0; i < _subImages.size(); i++)
desc += Common::String::format(" %d", (int16)_subImages[i].wdib);
}
@@ -496,7 +478,8 @@ const Common::String MystResourceType8::describe() {
// No MystResourceType9!
-MystResourceType10::MystResourceType10(MohawkEngine_Myst *vm, Common::SeekableReadStream *rlstStream, MystResource *parent) : MystResourceType11(vm, rlstStream, parent) {
+MystAreaSlider::MystAreaSlider(MohawkEngine_Myst *vm, Common::SeekableReadStream *rlstStream, MystArea *parent) :
+ MystAreaDrag(vm, rlstStream, parent) {
_dragSound = rlstStream->readUint16LE();
debugC(kDebugResource, "\tdrag sound : %d", _dragSound);
@@ -505,23 +488,23 @@ MystResourceType10::MystResourceType10(MohawkEngine_Myst *vm, Common::SeekableRe
_sliderHeight = _rect.bottom - _rect.top;
}
-MystResourceType10::~MystResourceType10() {
+MystAreaSlider::~MystAreaSlider() {
}
-void MystResourceType10::setStep(uint16 step) {
+void MystAreaSlider::setStep(uint16 step) {
_rect.top = _minV + _stepV * step - _sliderHeight / 2;
_rect.bottom = _rect.top + _sliderHeight;
_subImages[0].rect.top = 333 - _rect.bottom - 1;
_subImages[0].rect.bottom = 333 - _rect.top - 1;
}
-void MystResourceType10::setPosition(uint16 pos) {
+void MystAreaSlider::setPosition(uint16 pos) {
Common::Point mouse;
mouse.y = pos;
updatePosition(mouse);
}
-Common::Rect MystResourceType10::boundingBox() {
+Common::Rect MystAreaSlider::boundingBox() {
Common::Rect bb;
bb.top = _rect.top;
@@ -544,7 +527,7 @@ Common::Rect MystResourceType10::boundingBox() {
return bb;
}
-void MystResourceType10::restoreBackground() {
+void MystAreaSlider::restoreBackground() {
// Restore background
Common::Rect src = boundingBox();
Common::Rect dest = boundingBox();
@@ -553,14 +536,11 @@ void MystResourceType10::restoreBackground() {
_vm->_gfx->copyImageSectionToScreen(_vm->getCardBackgroundId(), src, dest);
}
-void MystResourceType10::handleMouseDown() {
- // Tell the engine we are dragging a resource
- _vm->_dragResource = this;
-
+void MystAreaSlider::handleMouseDown() {
const Common::Point &mouse = _vm->_system->getEventManager()->getMousePos();
updatePosition(mouse);
- MystResourceType11::handleMouseDown();
+ MystAreaDrag::handleMouseDown();
// Restore background
restoreBackground();
@@ -569,7 +549,7 @@ void MystResourceType10::handleMouseDown() {
drawConditionalDataToScreen(2);
}
-void MystResourceType10::handleMouseUp() {
+void MystAreaSlider::handleMouseUp() {
const Common::Point &mouse = _vm->_system->getEventManager()->getMousePos();
updatePosition(mouse);
@@ -593,19 +573,16 @@ void MystResourceType10::handleMouseUp() {
value = _pos.x;
}
- _vm->_scriptParser->setVarValue(_var8, value);
-
- MystResourceType11::handleMouseUp();
+ _vm->_scriptParser->setVarValue(_imageSwitchVar, value);
- // No longer in drag mode
- _vm->_dragResource = 0;
+ MystAreaDrag::handleMouseUp();
}
-void MystResourceType10::handleMouseDrag() {
+void MystAreaSlider::handleMouseDrag() {
const Common::Point &mouse = _vm->_system->getEventManager()->getMousePos();
updatePosition(mouse);
- MystResourceType11::handleMouseDrag();
+ MystAreaDrag::handleMouseDrag();
// Restore background
restoreBackground();
@@ -614,7 +591,7 @@ void MystResourceType10::handleMouseDrag() {
drawConditionalDataToScreen(2);
}
-void MystResourceType10::updatePosition(const Common::Point &mouse) {
+void MystAreaSlider::updatePosition(const Common::Point &mouse) {
bool positionChanged = false;
Common::Point mouseClipped;
@@ -667,7 +644,8 @@ void MystResourceType10::updatePosition(const Common::Point &mouse) {
_vm->_sound->replaceSoundMyst(_dragSound);
}
-MystResourceType11::MystResourceType11(MohawkEngine_Myst *vm, Common::SeekableReadStream *rlstStream, MystResource *parent) : MystResourceType8(vm, rlstStream, parent) {
+MystAreaDrag::MystAreaDrag(MohawkEngine_Myst *vm, Common::SeekableReadStream *rlstStream, MystArea *parent) :
+ MystAreaImageSwitch(vm, rlstStream, parent) {
_flagHV = rlstStream->readUint16LE();
_minH = rlstStream->readUint16LE();
_maxH = rlstStream->readUint16LE();
@@ -694,16 +672,15 @@ MystResourceType11::MystResourceType11(MohawkEngine_Myst *vm, Common::SeekableRe
debugCN(kDebugResource, "Type 11 _mouseDragOpcode: %d\n", _mouseDragOpcode);
debugCN(kDebugResource, "Type 11 _mouseUpOpcode: %d\n", _mouseUpOpcode);
- for (byte i = 0; i < 3; i++) {
+ for (byte i = 0; i < ARRAYSIZE(_lists); i++) {
debugC(kDebugResource, "\tList %d:", i);
- _lists[i].listCount = rlstStream->readUint16LE();
- debugC(kDebugResource, "\t%d values", _lists[i].listCount);
+ uint16 listCount = rlstStream->readUint16LE();
+ debugC(kDebugResource, "\t%d values", listCount);
- _lists[i].list = new uint16[_lists[i].listCount];
- for (uint16 j = 0; j < _lists[i].listCount; j++) {
- _lists[i].list[j] = rlstStream->readUint16LE();
- debugC(kDebugResource, "\tValue %d: %d", j, _lists[i].list[j]);
+ for (uint16 j = 0; j < listCount; j++) {
+ _lists[i].push_back(rlstStream->readUint16LE());
+ debugC(kDebugResource, "\tValue %d: %d", j, _lists[i][j]);
}
}
@@ -717,44 +694,42 @@ MystResourceType11::MystResourceType11(MohawkEngine_Myst *vm, Common::SeekableRe
_stepV = (_maxV - _minV) / (_stepsV - 1);
}
-MystResourceType11::~MystResourceType11() {
- for (byte i = 0; i < 3; i++)
- delete[] _lists[i].list;
+MystAreaDrag::~MystAreaDrag() {
}
-void MystResourceType11::handleMouseDown() {
+void MystAreaDrag::handleMouseDown() {
const Common::Point &mouse = _vm->_system->getEventManager()->getMousePos();
setPositionClipping(mouse, _pos);
_vm->_scriptParser->setInvokingResource(this);
- _vm->_scriptParser->runOpcode(_mouseDownOpcode, _var8);
+ _vm->_scriptParser->runOpcode(_mouseDownOpcode, _imageSwitchVar);
}
-void MystResourceType11::handleMouseUp() {
+void MystAreaDrag::handleMouseUp() {
const Common::Point &mouse = _vm->_system->getEventManager()->getMousePos();
setPositionClipping(mouse, _pos);
_vm->_scriptParser->setInvokingResource(this);
- _vm->_scriptParser->runOpcode(_mouseUpOpcode, _var8);
+ _vm->_scriptParser->runOpcode(_mouseUpOpcode, _imageSwitchVar);
}
-void MystResourceType11::handleMouseDrag() {
+void MystAreaDrag::handleMouseDrag() {
const Common::Point &mouse = _vm->_system->getEventManager()->getMousePos();
setPositionClipping(mouse, _pos);
_vm->_scriptParser->setInvokingResource(this);
- _vm->_scriptParser->runOpcode(_mouseDragOpcode, _var8);
+ _vm->_scriptParser->runOpcode(_mouseDragOpcode, _imageSwitchVar);
}
-const Common::String MystResourceType11::describe() {
+const Common::String MystAreaDrag::describe() {
return Common::String::format("%s down: %s drag: %s up: %s",
- MystResourceType8::describe().c_str(),
+ MystAreaImageSwitch::describe().c_str(),
_vm->_scriptParser->getOpcodeDesc(_mouseDownOpcode).c_str(),
_vm->_scriptParser->getOpcodeDesc(_mouseDragOpcode).c_str(),
_vm->_scriptParser->getOpcodeDesc(_mouseUpOpcode).c_str());
}
-void MystResourceType11::setPositionClipping(const Common::Point &mouse, Common::Point &dest) {
+void MystAreaDrag::setPositionClipping(const Common::Point &mouse, Common::Point &dest) {
if (_flagHV & 2)
dest.y = CLIP<uint16>(mouse.y, _minV, _maxV);
@@ -762,19 +737,20 @@ void MystResourceType11::setPositionClipping(const Common::Point &mouse, Common:
dest.x = CLIP<uint16>(mouse.x, _minH, _maxH);
}
-uint16 MystResourceType11::getList1(uint16 index) {
- return (index < _lists[0].listCount) ? _lists[0].list[index] : 0;
+uint16 MystAreaDrag::getList1(uint16 index) {
+ return (index < _lists[0].size()) ? _lists[0][index] : 0;
}
-uint16 MystResourceType11::getList2(uint16 index) {
- return (index < _lists[1].listCount) ? _lists[1].list[index] : 0;
+uint16 MystAreaDrag::getList2(uint16 index) {
+ return (index < _lists[1].size()) ? _lists[1][index] : 0;
}
-uint16 MystResourceType11::getList3(uint16 index) {
- return (index < _lists[2].listCount) ? _lists[2].list[index] : 0;
+uint16 MystAreaDrag::getList3(uint16 index) {
+ return (index < _lists[2].size()) ? _lists[2][index] : 0;
}
-MystResourceType12::MystResourceType12(MohawkEngine_Myst *vm, Common::SeekableReadStream *rlstStream, MystResource *parent) : MystResourceType11(vm, rlstStream, parent) {
+MystVideoInfo::MystVideoInfo(MohawkEngine_Myst *vm, Common::SeekableReadStream *rlstStream, MystArea *parent) :
+ MystAreaDrag(vm, rlstStream, parent) {
_numFrames = rlstStream->readUint16LE();
_firstFrame = rlstStream->readUint16LE();
uint16 frameWidth = rlstStream->readUint16LE();
@@ -795,16 +771,16 @@ MystResourceType12::MystResourceType12(MohawkEngine_Myst *vm, Common::SeekableRe
debugC(kDebugResource, "\t_frameRect.bottom: %d", _frameRect.bottom);
}
-MystResourceType12::~MystResourceType12() {
+MystVideoInfo::~MystVideoInfo() {
}
-void MystResourceType12::drawFrame(uint16 frame) {
+void MystVideoInfo::drawFrame(uint16 frame) {
_currentFrame = _firstFrame + frame;
_vm->_gfx->copyImageToScreen(_currentFrame, _frameRect);
_vm->_system->updateScreen();
}
-bool MystResourceType12::pullLeverV() {
+bool MystVideoInfo::pullLeverV() {
const Common::Point &mouse = _vm->_system->getEventManager()->getMousePos();
// Make the handle follow the mouse
@@ -820,7 +796,7 @@ bool MystResourceType12::pullLeverV() {
return step == maxStep;
}
-void MystResourceType12::releaseLeverV() {
+void MystVideoInfo::releaseLeverV() {
const Common::Point &mouse = _vm->_system->getEventManager()->getMousePos();
// Get current lever frame
@@ -836,7 +812,8 @@ void MystResourceType12::releaseLeverV() {
}
}
-MystResourceType13::MystResourceType13(MohawkEngine_Myst *vm, Common::SeekableReadStream *rlstStream, MystResource *parent) : MystResource(vm, rlstStream, parent) {
+MystAreaHover::MystAreaHover(MohawkEngine_Myst *vm, Common::SeekableReadStream *rlstStream, MystArea *parent) :
+ MystArea(vm, rlstStream, parent) {
_enterOpcode = rlstStream->readUint16LE();
_leaveOpcode = rlstStream->readUint16LE();
@@ -844,27 +821,27 @@ MystResourceType13::MystResourceType13(MohawkEngine_Myst *vm, Common::SeekableRe
debugC(kDebugResource, "\t_leaveOpcode: %d", _leaveOpcode);
}
-void MystResourceType13::handleMouseEnter() {
+void MystAreaHover::handleMouseEnter() {
// Pass along the enter opcode to the script parser
// The variable to use is stored in the dest field
_vm->_scriptParser->runOpcode(_enterOpcode, _dest);
}
-void MystResourceType13::handleMouseLeave() {
+void MystAreaHover::handleMouseLeave() {
// Pass along the leave opcode (with no parameters) to the script parser
// The variable to use is stored in the dest field
_vm->_scriptParser->runOpcode(_leaveOpcode, _dest);
}
-void MystResourceType13::handleMouseUp() {
+void MystAreaHover::handleMouseUp() {
// Type 13 Resources do nothing on Mouse Clicks.
// This is required to override the inherited default
- // i.e. MystResource::handleMouseUp
+ // i.e. MystArea::handleMouseUp
}
-const Common::String MystResourceType13::describe() {
+const Common::String MystAreaHover::describe() {
return Common::String::format("%s enter: %s leave: %s",
- MystResource::describe().c_str(),
+ MystArea::describe().c_str(),
_vm->_scriptParser->getOpcodeDesc(_enterOpcode).c_str(),
_vm->_scriptParser->getOpcodeDesc(_leaveOpcode).c_str());
}
diff --git a/engines/mohawk/myst_areas.h b/engines/mohawk/myst_areas.h
index 97ec882497..b19a2df9e2 100644
--- a/engines/mohawk/myst_areas.h
+++ b/engines/mohawk/myst_areas.h
@@ -32,19 +32,19 @@ namespace Mohawk {
// Myst Resource Types
enum ResourceType {
- kMystForwardArea = 0,
- kMystLeftArea = 1,
- kMystRightArea = 2,
- kMystDownArea = 3,
- kMystUpArea = 4,
- kMystAction = 5,
- kMystVideo = 6,
- kMystSwitch = 7,
- kMystConditionalImage = 8,
- kMystSlider = 10,
- kMystDragArea = 11,
+ kMystAreaForward = 0,
+ kMystAreaLeft = 1,
+ kMystAreaRight = 2,
+ kMystAreaDown = 3,
+ kMystAreaUp = 4,
+ kMystAreaAction = 5,
+ kMystAreaVideo = 6,
+ kMystAreaActionSwitch = 7,
+ kMystAreaImageSwitch = 8,
+ kMystAreaSlider = 10,
+ kMystAreaDrag = 11,
kMystVideoInfo = 12,
- kMystHoverArea = 13
+ kMystAreaHover = 13
};
// Myst Resource Flags
@@ -56,16 +56,14 @@ enum {
kMystZipModeEnableFlag = (1 << 3)
};
-class MystResource {
+class MystArea {
public:
- MystResource(MohawkEngine_Myst *vm, Common::SeekableReadStream *rlstStream, MystResource *parent);
- virtual ~MystResource();
+ MystArea(MohawkEngine_Myst *vm, Common::SeekableReadStream *rlstStream, MystArea *parent);
+ virtual ~MystArea();
+
virtual const Common::String describe();
void drawBoundingRect();
- MystResource *_parent;
- ResourceType type;
-
bool contains(Common::Point point) { return _rect.contains(point); }
virtual void drawDataToScreen() {}
virtual void handleCardChange() {}
@@ -75,7 +73,7 @@ public:
void setEnabled(bool enabled);
bool isDrawSubimages() { return _flags & kMystSubimageEnableFlag; }
uint16 getDest() { return _dest; }
- virtual uint16 getType8Var() { return 0xFFFF; }
+ virtual uint16 getImageSwitchVar() { return 0xFFFF; }
bool unreachableZipDest();
bool canBecomeActive();
@@ -84,6 +82,8 @@ public:
virtual void handleMouseDown() {}
virtual void handleMouseDrag() {}
+ MystArea *_parent;
+ ResourceType type;
protected:
MohawkEngine_Myst *_vm;
@@ -92,21 +92,25 @@ protected:
uint16 _dest;
};
-class MystResourceType5 : public MystResource {
+class MystAreaAction : public MystArea {
public:
- MystResourceType5(MohawkEngine_Myst *vm, Common::SeekableReadStream *rlstStream, MystResource *parent);
- void handleMouseUp();
- const Common::String describe();
+ MystAreaAction(MohawkEngine_Myst *vm, Common::SeekableReadStream *rlstStream, MystArea *parent);
+
+ void handleMouseUp() override;
+ const Common::String describe() override;
protected:
MystScript _script;
};
-class MystResourceType6 : public MystResourceType5 {
+class MystAreaVideo : public MystAreaAction {
public:
- MystResourceType6(MohawkEngine_Myst *vm, Common::SeekableReadStream *rlstStream, MystResource *parent);
+ MystAreaVideo(MohawkEngine_Myst *vm, Common::SeekableReadStream *rlstStream, MystArea *parent);
+
VideoHandle playMovie();
- void handleCardChange();
+ VideoHandle getMovieHandle();
+
+ void handleCardChange() override;
bool isPlaying();
void setDirection(int16 direction) { _direction = direction; }
void setBlocking(bool blocking) { _playBlocking = blocking; }
@@ -114,6 +118,7 @@ public:
protected:
static Common::String convertMystVideoName(Common::String name);
+
Common::String _videoFile;
int16 _left;
int16 _top;
@@ -121,58 +126,63 @@ protected:
int16 _direction; // 1 => forward, -1 => backwards
uint16 _playBlocking;
uint16 _playOnCardChange;
- uint16 _u3;
+ uint16 _playRate; // percents
};
-class MystResourceType7 : public MystResource {
+class MystAreaActionSwitch : public MystArea {
public:
- MystResourceType7(MohawkEngine_Myst *vm, Common::SeekableReadStream *rlstStream, MystResource *parent);
- virtual ~MystResourceType7();
+ MystAreaActionSwitch(MohawkEngine_Myst *vm, Common::SeekableReadStream *rlstStream, MystArea *parent);
+ virtual ~MystAreaActionSwitch();
- virtual void drawDataToScreen();
- virtual void handleCardChange();
+ virtual void drawDataToScreen() override;
+ virtual void handleCardChange() override;
- virtual void handleMouseUp();
- virtual void handleMouseDown();
+ virtual void handleMouseUp() override;
+ virtual void handleMouseDown() override;
- MystResource *getSubResource(uint16 index) { return _subResources[index]; }
+ MystArea *getSubResource(uint16 index) { return _subResources[index]; }
protected:
- uint16 _var7;
- uint16 _numSubResources;
- Common::Array<MystResource *> _subResources;
+ typedef void (MystArea::*AreaHandler)();
+
+ void doSwitch(AreaHandler handler);
+
+ uint16 _actionSwitchVar;
+ Common::Array<MystArea *> _subResources;
};
-class MystResourceType8 : public MystResourceType7 {
+class MystAreaImageSwitch : public MystAreaActionSwitch {
public:
- MystResourceType8(MohawkEngine_Myst *vm, Common::SeekableReadStream *rlstStream, MystResource *parent);
- virtual ~MystResourceType8();
- virtual const Common::String describe();
-
- virtual void drawDataToScreen();
- void drawConditionalDataToScreen(uint16 state, bool update = true);
- uint16 getType8Var();
+ MystAreaImageSwitch(MohawkEngine_Myst *vm, Common::SeekableReadStream *rlstStream, MystArea *parent);
+ virtual ~MystAreaImageSwitch();
struct SubImage {
uint16 wdib;
Common::Rect rect;
- } *_subImages;
+ };
+
+ virtual const Common::String describe() override;
+ virtual void drawDataToScreen() override;
+ void drawConditionalDataToScreen(uint16 state, bool update = true);
+ uint16 getImageSwitchVar() override;
+
+ SubImage getSubImage(uint index) const;
+ void setSubImageRect(uint index, const Common::Rect &rect);
protected:
- uint16 _var8;
- uint16 _numSubImages;
+ uint16 _imageSwitchVar;
+ Common::Array<SubImage> _subImages;
};
-// No MystResourceType9!
-
-class MystResourceType11 : public MystResourceType8 {
+class MystAreaDrag : public MystAreaImageSwitch {
public:
- MystResourceType11(MohawkEngine_Myst *vm, Common::SeekableReadStream *rlstStream, MystResource *parent);
- virtual ~MystResourceType11();
- const Common::String describe();
+ MystAreaDrag(MohawkEngine_Myst *vm, Common::SeekableReadStream *rlstStream, MystArea *parent);
+ virtual ~MystAreaDrag();
- void handleMouseDown();
- void handleMouseUp();
- void handleMouseDrag();
+ const Common::String describe() override;
+
+ virtual void handleMouseDown() override;
+ virtual void handleMouseUp() override;
+ virtual void handleMouseDrag() override;
uint16 getList1(uint16 index);
uint16 getList2(uint16 index);
@@ -183,6 +193,8 @@ public:
Common::Point _pos;
protected:
+ typedef Common::Array<uint16> ValueList;
+
void setPositionClipping(const Common::Point &mouse, Common::Point &dest);
uint16 _flagHV;
@@ -197,21 +209,17 @@ protected:
uint16 _mouseDownOpcode;
uint16 _mouseDragOpcode;
uint16 _mouseUpOpcode;
- struct {
- uint16 listCount;
- uint16 *list;
- } _lists[3];
-
+ ValueList _lists[3];
};
-class MystResourceType10 : public MystResourceType11 {
+class MystAreaSlider : public MystAreaDrag {
public:
- MystResourceType10(MohawkEngine_Myst *vm, Common::SeekableReadStream *rlstStream, MystResource *parent);
- virtual ~MystResourceType10();
+ MystAreaSlider(MohawkEngine_Myst *vm, Common::SeekableReadStream *rlstStream, MystArea *parent);
+ virtual ~MystAreaSlider();
- void handleMouseDown();
- void handleMouseUp();
- void handleMouseDrag();
+ void handleMouseDown() override;
+ void handleMouseUp() override;
+ void handleMouseDrag() override;
void setStep(uint16 step);
void setPosition(uint16 pos);
void restoreBackground();
@@ -225,10 +233,11 @@ protected:
uint16 _sliderHeight;
};
-class MystResourceType12 : public MystResourceType11 {
+class MystVideoInfo : public MystAreaDrag {
public:
- MystResourceType12(MohawkEngine_Myst *vm, Common::SeekableReadStream *rlstStream, MystResource *parent);
- virtual ~MystResourceType12();
+ MystVideoInfo(MohawkEngine_Myst *vm, Common::SeekableReadStream *rlstStream, MystArea *parent);
+ virtual ~MystVideoInfo();
+
void drawFrame(uint16 frame);
bool pullLeverV();
void releaseLeverV();
@@ -243,12 +252,13 @@ private:
uint16 _currentFrame;
};
-class MystResourceType13 : public MystResource {
+class MystAreaHover : public MystArea {
public:
- MystResourceType13(MohawkEngine_Myst *vm, Common::SeekableReadStream *rlstStream, MystResource *parent);
- const Common::String describe();
+ MystAreaHover(MohawkEngine_Myst *vm, Common::SeekableReadStream *rlstStream, MystArea *parent);
+
+ const Common::String describe() override;
- void handleMouseUp();
+ void handleMouseUp() override;
void handleMouseEnter();
void handleMouseLeave();
diff --git a/engines/mohawk/myst_graphics.cpp b/engines/mohawk/myst_graphics.cpp
index 49f97cca63..5db9697a78 100644
--- a/engines/mohawk/myst_graphics.cpp
+++ b/engines/mohawk/myst_graphics.cpp
@@ -40,7 +40,7 @@ MystGraphics::MystGraphics(MohawkEngine_Myst* vm) : GraphicsManager(), _vm(vm) {
if (_vm->getFeatures() & GF_ME) {
// High color
- initGraphics(_viewport.width(), _viewport.height(), true, NULL);
+ initGraphics(_viewport.width(), _viewport.height(), true, nullptr);
if (_vm->_system->getScreenFormat().bytesPerPixel == 1)
error("Myst ME requires greater than 256 colors to run");
@@ -73,7 +73,7 @@ MohawkSurface *MystGraphics::decodeImage(uint16 id) {
// if it's a PICT or WDIB resource. If it's Myst ME it's most likely a PICT, and if it's
// original it's definitely a WDIB. However, Myst ME throws us another curve ball in
// that PICT resources can contain WDIB's instead of PICT's.
- Common::SeekableReadStream *dataStream = NULL;
+ Common::SeekableReadStream *dataStream = nullptr;
if (_vm->getFeatures() & GF_ME && _vm->hasResource(ID_PICT, id)) {
// The PICT resource exists. However, it could still contain a MystBitmap
@@ -95,7 +95,7 @@ MohawkSurface *MystGraphics::decodeImage(uint16 id) {
dataStream->seek(0);
}
- MohawkSurface *mhkSurface = 0;
+ MohawkSurface *mhkSurface = nullptr;
if (isPict) {
Image::PICTDecoder pict;
@@ -103,6 +103,8 @@ MohawkSurface *MystGraphics::decodeImage(uint16 id) {
if (!pict.loadStream(*dataStream))
error("Could not decode Myst ME PICT");
+ delete dataStream;
+
mhkSurface = new MohawkSurface(pict.getSurface()->convertTo(_pixelFormat));
} else {
mhkSurface = _bmpDecoder->decodeImage(dataStream);
@@ -225,9 +227,8 @@ void MystGraphics::copyBackBufferToScreen(Common::Rect r) {
void MystGraphics::runTransition(TransitionType type, Common::Rect rect, uint16 steps, uint16 delay) {
- // Do not artificially delay during transitions
- int oldEnableDrawingTimeSimulation = _enableDrawingTimeSimulation;
- _enableDrawingTimeSimulation = 0;
+ // Transitions are barely visible without adding delays between the draw calls
+ enableDrawingTimeSimulation(true);
switch (type) {
case kTransitionLeftToRight: {
@@ -288,7 +289,10 @@ void MystGraphics::runTransition(TransitionType type, Common::Rect rect, uint16
debugC(kDebugView, "Dissolve");
for (int16 step = 0; step < 8; step++) {
- simulatePreviousDrawDelay(rect);
+ // Only one eighth of the rect pixels are updated by a draw step,
+ // delay by one eighth of the regular time
+ simulatePreviousDrawDelay(Common::Rect(rect.width() / 8, rect.height()));
+
transitionDissolve(rect, step);
}
}
@@ -367,7 +371,7 @@ void MystGraphics::runTransition(TransitionType type, Common::Rect rect, uint16
error("Unknown transition %d", type);
}
- _enableDrawingTimeSimulation = oldEnableDrawingTimeSimulation;
+ enableDrawingTimeSimulation(false);
}
void MystGraphics::transitionDissolve(Common::Rect rect, uint step) {
@@ -639,8 +643,10 @@ void MystGraphics::simulatePreviousDrawDelay(const Common::Rect &dest) {
// Do not draw anything new too quickly after the previous draw call
// so that images stay at least a little while on screen
// This is enabled only for scripted draw calls
- if (time < _nextAllowedDrawTime)
+ if (time < _nextAllowedDrawTime) {
+ debugC(kDebugView, "Delaying draw call by %d ms", _nextAllowedDrawTime - time);
_vm->_system->delayMillis(_nextAllowedDrawTime - time);
+ }
}
// Next draw call allowed at DELAY + AERA * COEFF milliseconds from now
diff --git a/engines/mohawk/myst_graphics.h b/engines/mohawk/myst_graphics.h
index 6281c94cc8..93e388cb83 100644
--- a/engines/mohawk/myst_graphics.h
+++ b/engines/mohawk/myst_graphics.h
@@ -61,8 +61,8 @@ public:
const byte *getPalette() const { return _palette; }
protected:
- MohawkSurface *decodeImage(uint16 id);
- MohawkEngine *getVM() { return (MohawkEngine *)_vm; }
+ MohawkSurface *decodeImage(uint16 id) override;
+ MohawkEngine *getVM() override { return (MohawkEngine *)_vm; }
private:
MohawkEngine_Myst *_vm;
diff --git a/engines/mohawk/myst_scripts.cpp b/engines/mohawk/myst_scripts.cpp
index 487d0f45fe..04e7c5a9b7 100644
--- a/engines/mohawk/myst_scripts.cpp
+++ b/engines/mohawk/myst_scripts.cpp
@@ -29,6 +29,7 @@
#include "mohawk/video.h"
#include "common/system.h"
+#include "common/memstream.h"
#include "common/textconsole.h"
#include "gui/message.h"
@@ -38,7 +39,7 @@ MystScriptEntry::MystScriptEntry() {
type = kMystScriptNone;
var = 0;
argc = 0;
- argv = 0;
+ argv = nullptr;
resourceId = 0;
u1 = 0;
}
@@ -81,7 +82,7 @@ MystScriptParser::MystScriptParser(MohawkEngine_Myst *vm) :
_vm(vm),
_globals(vm->_gameState->_globals) {
setupCommonOpcodes();
- _invokingResource = NULL;
+ _invokingResource = nullptr;
_savedCardId = 0;
_savedCursorId = 0;
_tempVar = 0;
@@ -154,7 +155,7 @@ void MystScriptParser::setupCommonOpcodes() {
#undef OPCODE
-void MystScriptParser::runScript(MystScript script, MystResource *invokingResource) {
+void MystScriptParser::runScript(MystScript script, MystArea *invokingResource) {
debugC(kDebugScript, "Script Size: %d", script->size());
// Scripted drawing takes more time to simulate older hardware
@@ -260,15 +261,6 @@ bool MystScriptParser::setVarValue(uint16 var, uint16 value) {
return false;
}
-// NOTE: Check to be used on Opcodes where var is thought
-// not to be used. This emits a warning if var is nonzero.
-// It is possible that the opcode does use var 0 in this case,
-// but this will catch the majority of missed cases.
-void MystScriptParser::varUnusedCheck(uint16 op, uint16 var) {
- if (var != 0)
- warning("Opcode %d: Unused Var %d", op, var);
-}
-
void MystScriptParser::animatedUpdate(uint16 argc, uint16 *argv, uint16 delay) {
uint16 argsRead = 0;
@@ -331,7 +323,7 @@ void MystScriptParser::o_changeCardSwitch4(uint16 op, uint16 var, uint16 argc, u
if (value)
_vm->changeToCard(argv[value -1 ], kTransitionDissolve);
- else if (_invokingResource != NULL)
+ else if (_invokingResource != nullptr)
_vm->changeToCard(_invokingResource->getDest(), kTransitionDissolve);
else
warning("Missing invokingResource in altDest call");
@@ -344,7 +336,7 @@ void MystScriptParser::o_changeCardSwitchLtR(uint16 op, uint16 var, uint16 argc,
if (value)
_vm->changeToCard(argv[value -1 ], kTransitionLeftToRight);
- else if (_invokingResource != NULL)
+ else if (_invokingResource != nullptr)
_vm->changeToCard(_invokingResource->getDest(), kTransitionLeftToRight);
else
warning("Missing invokingResource in altDest call");
@@ -357,7 +349,7 @@ void MystScriptParser::o_changeCardSwitchRtL(uint16 op, uint16 var, uint16 argc,
if (value)
_vm->changeToCard(argv[value -1 ], kTransitionRightToLeft);
- else if (_invokingResource != NULL)
+ else if (_invokingResource != nullptr)
_vm->changeToCard(_invokingResource->getDest(), kTransitionRightToLeft);
else
warning("Missing invokingResource in altDest call");
@@ -398,7 +390,7 @@ void MystScriptParser::o_redrawCard(uint16 op, uint16 var, uint16 argc, uint16 *
void MystScriptParser::o_goToDest(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
debugC(kDebugScript, "Opcode %d: Change To Dest of Invoking Resource", op);
- if (_invokingResource != NULL)
+ if (_invokingResource != nullptr)
_vm->changeToCard(_invokingResource->getDest(), kTransitionCopy);
else
warning("Opcode %d: Missing invokingResource", op);
@@ -407,7 +399,7 @@ void MystScriptParser::o_goToDest(uint16 op, uint16 var, uint16 argc, uint16 *ar
void MystScriptParser::o_goToDestForward(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
debugC(kDebugScript, "Opcode %d: Change To Dest of Invoking Resource", op);
- if (_invokingResource != NULL)
+ if (_invokingResource != nullptr)
_vm->changeToCard(_invokingResource->getDest(), kTransitionDissolve);
else
warning("Opcode %d: Missing invokingResource", op);
@@ -416,7 +408,7 @@ void MystScriptParser::o_goToDestForward(uint16 op, uint16 var, uint16 argc, uin
void MystScriptParser::o_goToDestLeft(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
debugC(kDebugScript, "Opcode %d: Change To Dest of Invoking Resource", op);
- if (_invokingResource != NULL)
+ if (_invokingResource != nullptr)
_vm->changeToCard(_invokingResource->getDest(), kTransitionPartToRight);
else
warning("Opcode %d: Missing invokingResource", op);
@@ -425,7 +417,7 @@ void MystScriptParser::o_goToDestLeft(uint16 op, uint16 var, uint16 argc, uint16
void MystScriptParser::o_goToDestRight(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
debugC(kDebugScript, "Opcode %d: Change To Dest of Invoking Resource", op);
- if (_invokingResource != NULL)
+ if (_invokingResource != nullptr)
_vm->changeToCard(_invokingResource->getDest(), kTransitionPartToLeft);
else
warning("Opcode %d: Missing invokingResource", op);
@@ -434,7 +426,7 @@ void MystScriptParser::o_goToDestRight(uint16 op, uint16 var, uint16 argc, uint1
void MystScriptParser::o_goToDestUp(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
debugC(kDebugScript, "Opcode %d: Change To Dest of Invoking Resource", op);
- if (_invokingResource != NULL)
+ if (_invokingResource != nullptr)
_vm->changeToCard(_invokingResource->getDest(), kTransitionTopToBottom);
else
warning("Opcode %d: Missing invokingResource", op);
@@ -442,7 +434,10 @@ void MystScriptParser::o_goToDestUp(uint16 op, uint16 var, uint16 argc, uint16 *
void MystScriptParser::o_triggerMovie(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
debugC(kDebugScript, "Opcode %d: Trigger Type 6 Resource Movie..", op);
- // TODO: If movie has sound, pause background music
+ // The original has code to pause the background music before playing the movie,
+ // if the movie has a sound track, as well as code to resume it afterwards. But since
+ // the movie has not yet been loaded at this point, it is impossible to know
+ // if the movie actually has a sound track. The code is never executed.
int16 direction = 1;
if (argc == 1)
@@ -451,11 +446,9 @@ void MystScriptParser::o_triggerMovie(uint16 op, uint16 var, uint16 argc, uint16
debugC(kDebugScript, "\tDirection: %d", direction);
// Trigger resource 6 movie overriding play direction
- MystResourceType6 *resource = static_cast<MystResourceType6 *>(_invokingResource);
+ MystAreaVideo *resource = getInvokingResource<MystAreaVideo>();
resource->setDirection(direction);
resource->playMovie();
-
- // TODO: If movie has sound, resume background music
}
void MystScriptParser::o_toggleVarNoRedraw(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
@@ -468,7 +461,7 @@ void MystScriptParser::o_drawAreaState(uint16 op, uint16 var, uint16 argc, uint1
debugC(kDebugScript, "Opcode %d: drawAreaState, state: %d", op, argv[0]);
debugC(kDebugScript, "\tVar: %d", var);
- MystResourceType8 *parent = static_cast<MystResourceType8 *>(_invokingResource->_parent);
+ MystAreaImageSwitch *parent = static_cast<MystAreaImageSwitch *>(getInvokingResource<MystArea>()->_parent);
parent->drawConditionalDataToScreen(argv[0]);
}
@@ -517,6 +510,11 @@ void MystScriptParser::o_changeCardPop(uint16 op, uint16 var, uint16 argc, uint1
debugC(kDebugScript, "Opcode %d: Return To Stored Card Id", op);
debugC(kDebugScript, "\tCardId: %d", _savedCardId);
+ if (_savedCardId == 0) {
+ warning("No pushed card to go back to");
+ return;
+ }
+
TransitionType transition = static_cast<TransitionType>(argv[0]);
_vm->changeToCard(_savedCardId, transition);
@@ -531,7 +529,7 @@ void MystScriptParser::o_enableAreas(uint16 op, uint16 var, uint16 argc, uint16
for (uint16 i = 0; i < count; i++) {
debugC(kDebugScript, "Enable hotspot index %d", argv[i + 1]);
- MystResource *resource = 0;
+ MystArea *resource = nullptr;
if (argv[i + 1] == 0xFFFF)
resource = _invokingResource;
else
@@ -556,7 +554,7 @@ void MystScriptParser::o_disableAreas(uint16 op, uint16 var, uint16 argc, uint16
for (uint16 i = 0; i < count; i++) {
debugC(kDebugScript, "Disable hotspot index %d", argv[i + 1]);
- MystResource *resource = 0;
+ MystArea *resource = nullptr;
if (argv[i + 1] == 0xFFFF)
resource = _invokingResource;
else
@@ -587,7 +585,7 @@ void MystScriptParser::o_toggleAreasActivation(uint16 op, uint16 var, uint16 arg
for (uint16 i = 0; i < count; i++) {
debugC(kDebugScript, "Enable/Disable hotspot index %d", argv[i + 1]);
- MystResource *resource = 0;
+ MystArea *resource = nullptr;
if (argv[i + 1] == 0xFFFF)
resource = _invokingResource;
else
@@ -682,82 +680,15 @@ void MystScriptParser::o_copyImageToBackBuffer(uint16 op, uint16 var, uint16 arg
_vm->_gfx->copyImageSectionToBackBuffer(imageId, srcRect, dstRect);
}
-// TODO: Implement common engine function for read and processing of sound blocks
-// for use by this opcode and VIEW sound block.
-// TODO: Though the playSound and PlaySoundBlocking opcodes play sounds immediately,
-// this opcode changes the main background sound playing..
-// Current behavior here and with VIEW sound block is not right as demonstrated
-// by Channelwood Card 3280 (Tank Valve) and water flow sound behavior in pipe
-// on cards leading from shed...
void MystScriptParser::o_changeBackgroundSound(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
- int16 *soundList = NULL;
- uint16 *soundListVolume = NULL;
-
// Used on Stoneship Card 2080
// Used on Channelwood Card 3225 with argc = 8 i.e. Conditional Sound List
- if (argc == 1 || argc == 2 || argc == 8) {
- debugC(kDebugScript, "Opcode %d: Process Sound Block", op);
- uint16 decodeIdx = 0;
-
- int16 soundAction = argv[decodeIdx++];
- uint16 soundVolume = 65535;
- if (soundAction == kMystSoundActionChangeVolume || soundAction > 0) {
- soundVolume = argv[decodeIdx++];
- } else if (soundAction == kMystSoundActionConditional) {
- debugC(kDebugScript, "Conditional sound list");
- uint16 condVar = argv[decodeIdx++];
- uint16 condVarValue = getVar(condVar);
- uint16 condCount = argv[decodeIdx++];
-
- debugC(kDebugScript, "\tcondVar: %d = %d", condVar, condVarValue);
- debugC(kDebugScript, "\tcondCount: %d", condCount);
-
- soundList = new int16[condCount];
- soundListVolume = new uint16[condCount];
-
- if (condVarValue >= condCount)
- warning("Opcode %d: Conditional sound variable outside range", op);
- else {
- for (uint16 i = 0; i < condCount; i++) {
- soundList[i] = argv[decodeIdx++];
- debugC(kDebugScript, "\t\tCondition %d: Action %d", i, soundList[i]);
- if (soundList[i] == kMystSoundActionChangeVolume || soundList[i] > 0) {
- soundListVolume[i] = argv[decodeIdx++];
- } else
- soundListVolume[i] = 65535;
- debugC(kDebugScript, "\t\tCondition %d: Volume %d", i, soundListVolume[i]);
- }
-
- soundAction = soundList[condVarValue];
- soundVolume = soundListVolume[condVarValue];
- }
- }
+ debugC(kDebugScript, "Opcode %d: Process Sound Block", op);
- if (soundAction == kMystSoundActionContinue)
- debugC(kDebugScript, "Continue current sound");
- else if (soundAction == kMystSoundActionChangeVolume) {
- debugC(kDebugScript, "Continue current sound, change volume");
- debugC(kDebugScript, "\tVolume: %d", soundVolume);
- _vm->_sound->changeBackgroundVolumeMyst(soundVolume);
- } else if (soundAction == kMystSoundActionStop) {
- debugC(kDebugScript, "Stop sound");
- _vm->_sound->stopBackgroundMyst();
- } else if (soundAction > 0) {
- debugC(kDebugScript, "Play new Sound, change volume");
- debugC(kDebugScript, "\tSound: %d", soundAction);
- debugC(kDebugScript, "\tVolume: %d", soundVolume);
- _vm->_sound->replaceBackgroundMyst(soundAction, soundVolume);
- } else {
- debugC(kDebugScript, "Unknown");
- warning("Unknown sound control value in opcode %d", op);
- }
- } else
- warning("Unknown arg count in opcode %d", op);
+ Common::MemoryReadStream stream = Common::MemoryReadStream((const byte *) argv, argc * sizeof(uint16));
- delete[] soundList;
- soundList = NULL;
- delete[] soundListVolume;
- soundListVolume = NULL;
+ MystSoundBlock soundBlock = _vm->readSoundBlock(&stream);
+ _vm->applySoundBlock(soundBlock);
}
void MystScriptParser::o_soundPlaySwitch(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
diff --git a/engines/mohawk/myst_scripts.h b/engines/mohawk/myst_scripts.h
index 7d8165c762..69052b10f5 100644
--- a/engines/mohawk/myst_scripts.h
+++ b/engines/mohawk/myst_scripts.h
@@ -34,7 +34,7 @@ namespace Mohawk {
#define DECLARE_OPCODE(x) void x(uint16 op, uint16 var, uint16 argc, uint16 *argv)
class MohawkEngine_Myst;
-class MystResource;
+class MystArea;
enum MystScriptType {
kMystScriptNone,
@@ -63,11 +63,11 @@ public:
MystScriptParser(MohawkEngine_Myst *vm);
virtual ~MystScriptParser();
- void runScript(MystScript script, MystResource *invokingResource = NULL);
- void runOpcode(uint16 op, uint16 var = 0, uint16 argc = 0, uint16 *argv = NULL);
+ void runScript(MystScript script, MystArea *invokingResource = nullptr);
+ void runOpcode(uint16 op, uint16 var = 0, uint16 argc = 0, uint16 *argv = nullptr);
const Common::String getOpcodeDesc(uint16 op);
MystScript readScript(Common::SeekableReadStream *stream, MystScriptType type);
- void setInvokingResource(MystResource *resource) { _invokingResource = resource; }
+ void setInvokingResource(MystArea *resource) { _invokingResource = resource; }
virtual void disablePersistentScripts() = 0;
virtual void runPersistentScripts() = 0;
@@ -151,8 +151,6 @@ protected:
Common::Array<MystOpcode *> _opcodes;
- MystResource *_invokingResource;
-
uint16 _savedCardId;
uint16 _savedMapCardId;
uint16 _savedCursorId;
@@ -163,9 +161,25 @@ protected:
static const uint16 _startCard[];
void setupCommonOpcodes();
- void varUnusedCheck(uint16 op, uint16 var);
+
+ template<class T>
+ T *getInvokingResource() const;
+
+private:
+ MystArea *_invokingResource;
};
+template<class T>
+T *MystScriptParser::getInvokingResource() const {
+ T *resource = dynamic_cast<T *>(_invokingResource);
+
+ if (!resource) {
+ error("Invoking resource has unexpected type");
+ }
+
+ return resource;
+}
+
} // End of namespace Mohawk
#undef DECLARE_OPCODE
diff --git a/engines/mohawk/myst_stacks/channelwood.cpp b/engines/mohawk/myst_stacks/channelwood.cpp
index 0718f8e683..21c3042359 100644
--- a/engines/mohawk/myst_stacks/channelwood.cpp
+++ b/engines/mohawk/myst_stacks/channelwood.cpp
@@ -363,7 +363,7 @@ void Channelwood::o_drawImageChangeCardAndVolume(uint16 op, uint16 var, uint16 a
void Channelwood::o_waterTankValveOpen(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
debugC(kDebugScript, "Opcode %d: Do Water Tank Valve Open Animation", op);
- Common::Rect rect = _invokingResource->getRect();
+ Common::Rect rect = getInvokingResource<MystArea>()->getRect();
for (uint i = 0; i < 2; i++)
for (uint16 imageId = 3601; imageId >= 3595; imageId--) {
@@ -377,7 +377,7 @@ void Channelwood::o_waterTankValveOpen(uint16 op, uint16 var, uint16 argc, uint1
void Channelwood::o_leverStartMove(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
debugC(kDebugScript, "Opcode %d: Generic lever start move", op);
- MystResourceType12 *lever = static_cast<MystResourceType12 *>(_invokingResource);
+ MystVideoInfo *lever = getInvokingResource<MystVideoInfo>();
lever->drawFrame(0);
_vm->_cursor->setCursor(700);
_leverPulled = false;
@@ -386,7 +386,7 @@ void Channelwood::o_leverStartMove(uint16 op, uint16 var, uint16 argc, uint16 *a
void Channelwood::o_leverMove(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
debugC(kDebugScript, "Opcode %d: Generic lever move", op);
- MystResourceType12 *lever = static_cast<MystResourceType12 *>(_invokingResource);
+ MystVideoInfo *lever = getInvokingResource<MystVideoInfo>();
if (lever->pullLeverV()) {
if (!_leverPulled) {
@@ -401,7 +401,7 @@ void Channelwood::o_leverMove(uint16 op, uint16 var, uint16 argc, uint16 *argv)
void Channelwood::o_leverMoveFail(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
debugC(kDebugScript, "Opcode %d: Generic lever move", op);
- MystResourceType12 *lever = static_cast<MystResourceType12 *>(_invokingResource);
+ MystVideoInfo *lever = getInvokingResource<MystVideoInfo>();
if (lever->pullLeverV()) {
if (!_leverPulled) {
@@ -419,7 +419,7 @@ void Channelwood::o_leverEndMove(uint16 op, uint16 var, uint16 argc, uint16 *arg
debugC(kDebugScript, "Opcode %d: Generic lever end move", op);
// Get current lever frame
- MystResourceType12 *lever = static_cast<MystResourceType12 *>(_invokingResource);
+ MystVideoInfo *lever = getInvokingResource<MystVideoInfo>();
// Release lever
lever->releaseLeverV();
@@ -439,7 +439,7 @@ void Channelwood::o_leverEndMoveResumeBackground(uint16 op, uint16 var, uint16 a
void Channelwood::o_leverEndMoveWithSound(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
o_leverEndMove(op, var, argc, argv);
- MystResourceType12 *lever = static_cast<MystResourceType12 *>(_invokingResource);
+ MystVideoInfo *lever = getInvokingResource<MystVideoInfo>();
uint16 soundId = lever->getList3(0);
if (soundId)
_vm->_sound->replaceSoundMyst(soundId);
@@ -461,7 +461,7 @@ void Channelwood::o_leverElev3EndMove(uint16 op, uint16 var, uint16 argc, uint16
void Channelwood::o_pumpLeverMove(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
debugC(kDebugScript, "Opcode %d: Pump lever move", op);
- MystResourceType12 *lever = static_cast<MystResourceType12 *>(_invokingResource);
+ MystVideoInfo *lever = getInvokingResource<MystVideoInfo>();
if (lever->pullLeverV()) {
uint16 soundId = lever->getList2(0);
@@ -475,7 +475,7 @@ void Channelwood::o_pumpLeverMove(uint16 op, uint16 var, uint16 argc, uint16 *ar
void Channelwood::o_pumpLeverEndMove(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
o_leverEndMove(op, var, argc, argv);
- MystResourceType12 *lever = static_cast<MystResourceType12 *>(_invokingResource);
+ MystVideoInfo *lever = getInvokingResource<MystVideoInfo>();
uint16 soundId = lever->getList3(0);
if (soundId)
_vm->_sound->replaceBackgroundMyst(soundId, 36864);
@@ -484,7 +484,7 @@ void Channelwood::o_pumpLeverEndMove(uint16 op, uint16 var, uint16 argc, uint16
void Channelwood::o_stairsDoorToggle(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
debugC(kDebugScript, "Opcode %d: Play stairs door video", op);
- MystResourceType6 *movie = static_cast<MystResourceType6 *>(_invokingResource);
+ MystAreaVideo *movie = getInvokingResource<MystAreaVideo>();
if (_state.stairsUpperDoorState) {
// Close door, play the open movie backwards
@@ -500,7 +500,7 @@ void Channelwood::o_stairsDoorToggle(uint16 op, uint16 var, uint16 argc, uint16
void Channelwood::o_valveHandleMove1(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
debugC(kDebugScript, "Opcode %d: Valve handle move", op);
- MystResourceType12 *handle = static_cast<MystResourceType12 *>(_invokingResource);
+ MystVideoInfo *handle = getInvokingResource<MystVideoInfo>();
const Common::Point &mouse = _vm->_system->getEventManager()->getMousePos();
if (handle->getRect().contains(mouse)) {
@@ -516,7 +516,7 @@ void Channelwood::o_valveHandleMove1(uint16 op, uint16 var, uint16 argc, uint16
void Channelwood::o_valveHandleMoveStart1(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
debugC(kDebugScript, "Opcode %d: Valve handle move start", op);
- MystResourceType12 *handle = static_cast<MystResourceType12 *>(_invokingResource);
+ MystVideoInfo *handle = getInvokingResource<MystVideoInfo>();
uint16 soundId = handle->getList1(0);
if (soundId)
_vm->_sound->replaceSoundMyst(soundId);
@@ -528,7 +528,7 @@ void Channelwood::o_valveHandleMoveStart1(uint16 op, uint16 var, uint16 argc, ui
void Channelwood::o_valveHandleMoveStop(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
debugC(kDebugScript, "Opcode %d: Valve handle move stop", op);
- MystResourceType12 *handle = static_cast<MystResourceType12 *>(_invokingResource);
+ MystVideoInfo *handle = getInvokingResource<MystVideoInfo>();
// Update state with valve position
if (_tempVar <= 5)
@@ -551,7 +551,7 @@ void Channelwood::o_valveHandleMoveStop(uint16 op, uint16 var, uint16 argc, uint
void Channelwood::o_valveHandleMove2(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
debugC(kDebugScript, "Opcode %d: Valve handle move", op);
- MystResourceType12 *handle = static_cast<MystResourceType12 *>(_invokingResource);
+ MystVideoInfo *handle = getInvokingResource<MystVideoInfo>();
const Common::Point &mouse = _vm->_system->getEventManager()->getMousePos();
if (handle->getRect().contains(mouse)) {
@@ -567,7 +567,7 @@ void Channelwood::o_valveHandleMove2(uint16 op, uint16 var, uint16 argc, uint16
void Channelwood::o_valveHandleMoveStart2(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
debugC(kDebugScript, "Opcode %d: Valve handle move start", op);
- MystResourceType12 *handle = static_cast<MystResourceType12 *>(_invokingResource);
+ MystVideoInfo *handle = getInvokingResource<MystVideoInfo>();
uint16 soundId = handle->getList1(0);
if (soundId)
_vm->_sound->replaceSoundMyst(soundId);
@@ -579,7 +579,7 @@ void Channelwood::o_valveHandleMoveStart2(uint16 op, uint16 var, uint16 argc, ui
void Channelwood::o_valveHandleMove3(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
debugC(kDebugScript, "Opcode %d: Valve handle move", op);
- MystResourceType12 *handle = static_cast<MystResourceType12 *>(_invokingResource);
+ MystVideoInfo *handle = getInvokingResource<MystVideoInfo>();
const Common::Point &mouse = _vm->_system->getEventManager()->getMousePos();
if (handle->getRect().contains(mouse)) {
@@ -595,7 +595,7 @@ void Channelwood::o_valveHandleMove3(uint16 op, uint16 var, uint16 argc, uint16
void Channelwood::o_valveHandleMoveStart3(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
debugC(kDebugScript, "Opcode %d: Valve handle move start", op);
- MystResourceType12 *handle = static_cast<MystResourceType12 *>(_invokingResource);
+ MystVideoInfo *handle = getInvokingResource<MystVideoInfo>();
uint16 soundId = handle->getList1(0);
if (soundId)
_vm->_sound->replaceSoundMyst(soundId);
@@ -621,24 +621,32 @@ void Channelwood::o_hologramMonitor(uint16 op, uint16 var, uint16 argc, uint16 *
switch (button) {
case 0:
handle = _vm->_video->playMovie(_vm->wrapMovieFilename("monalgh", kChannelwoodStack));
+ if (!handle)
+ error("Failed to open monalgh movie");
+ handle->moveTo(227, 70);
break;
case 1:
handle = _vm->_video->playMovie(_vm->wrapMovieFilename("monamth", kChannelwoodStack));
+ if (!handle)
+ error("Failed to open monamth movie");
+ handle->moveTo(227, 70);
break;
case 2:
handle = _vm->_video->playMovie(_vm->wrapMovieFilename("monasirs", kChannelwoodStack));
+ if (!handle)
+ error("Failed to open monasirs movie");
+ handle->moveTo(227, 70);
break;
case 3:
handle = _vm->_video->playMovie(_vm->wrapMovieFilename("monsmsg", kChannelwoodStack));
+ if (!handle)
+ error("Failed to open monsmsg movie");
+ handle->moveTo(226, 68);
break;
default:
warning("Opcode %d Control Variable Out of Range", op);
break;
}
-
- // Move the video to the right location
- if (handle)
- handle->moveTo(227, 70);
}
}
@@ -680,13 +688,13 @@ void Channelwood::o_hologramTemple(uint16 op, uint16 var, uint16 argc, uint16 *a
void Channelwood::o_executeMouseUp(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
debugC(kDebugScript, "Opcode %d: Execute mouse up", op);
- MystResourceType5 *resource = static_cast<MystResourceType5 *>(_vm->_resources[argv[0]]);
+ MystArea *resource = _vm->getViewResource<MystArea>(argv[0]);
resource->handleMouseUp();
}
void Channelwood::o_waterTankValveClose(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
debugC(kDebugScript, "Opcode %d: Do Water Tank Valve Close Animation", op);
- Common::Rect rect = _invokingResource->getRect();
+ Common::Rect rect = getInvokingResource<MystArea>()->getRect();
for (uint i = 0; i < 2; i++)
for (uint16 imageId = 3595; imageId <= 3601; imageId++) {
@@ -747,13 +755,14 @@ void Channelwood::o_soundReplace(uint16 op, uint16 var, uint16 argc, uint16 *arg
uint16 soundId = argv[0];
- // TODO: If is foreground playing
- _vm->_sound->replaceSoundMyst(soundId);
+ if (!_vm->_sound->isPlaying()) {
+ _vm->_sound->replaceSoundMyst(soundId);
+ }
}
void Channelwood::o_lever_init(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
debugC(kDebugScript, "Opcode %d: Generic lever init", op);
- _leverAction = static_cast<MystResourceType5 *>(_invokingResource);
+ _leverAction = getInvokingResource<MystArea>();
}
void Channelwood::o_pipeValve_init(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
diff --git a/engines/mohawk/myst_stacks/channelwood.h b/engines/mohawk/myst_stacks/channelwood.h
index bd5d7ffe94..ac875e52d8 100644
--- a/engines/mohawk/myst_stacks/channelwood.h
+++ b/engines/mohawk/myst_stacks/channelwood.h
@@ -40,16 +40,16 @@ public:
Channelwood(MohawkEngine_Myst *vm);
~Channelwood();
- void disablePersistentScripts();
- void runPersistentScripts();
+ void disablePersistentScripts() override;
+ void runPersistentScripts() override;
private:
void setupOpcodes();
- uint16 getVar(uint16 var);
- void toggleVar(uint16 var);
- bool setVarValue(uint16 var, uint16 value);
+ uint16 getVar(uint16 var) override;
+ void toggleVar(uint16 var) override;
+ bool setVarValue(uint16 var, uint16 value) override;
- virtual uint16 getMap() { return 9932; }
+ virtual uint16 getMap() override { return 9932; }
DECLARE_OPCODE(o_bridgeToggle);
DECLARE_OPCODE(o_pipeExtend);
@@ -94,7 +94,7 @@ private:
uint16 _doorOpened; // 68
bool _leverPulled;
- MystResourceType5 *_leverAction; // 72
+ MystArea *_leverAction; // 72
bool pipeChangeValve(bool open, uint16 mask);
};
diff --git a/engines/mohawk/myst_stacks/credits.cpp b/engines/mohawk/myst_stacks/credits.cpp
index b9ff8b26aa..b4a2076528 100644
--- a/engines/mohawk/myst_stacks/credits.cpp
+++ b/engines/mohawk/myst_stacks/credits.cpp
@@ -37,6 +37,7 @@ namespace MystStacks {
Credits::Credits(MohawkEngine_Myst *vm) : MystScriptParser(vm) {
setupOpcodes();
+ _curImage = 0;
}
Credits::~Credits() {
@@ -66,8 +67,10 @@ void Credits::runPersistentScripts() {
_curImage++;
// After the 6th image has shown, it's time to quit
- if (_curImage == 7)
+ if (_curImage == 7) {
_vm->quitGame();
+ return;
+ }
// Draw next image
_vm->drawCardBackground();
diff --git a/engines/mohawk/myst_stacks/credits.h b/engines/mohawk/myst_stacks/credits.h
index 3c0f969203..c2c20372bd 100644
--- a/engines/mohawk/myst_stacks/credits.h
+++ b/engines/mohawk/myst_stacks/credits.h
@@ -40,12 +40,12 @@ public:
Credits(MohawkEngine_Myst *vm);
~Credits();
- void disablePersistentScripts();
- void runPersistentScripts();
+ void disablePersistentScripts() override;
+ void runPersistentScripts() override;
private:
void setupOpcodes();
- uint16 getVar(uint16 var);
+ uint16 getVar(uint16 var) override;
DECLARE_OPCODE(o_runCredits);
diff --git a/engines/mohawk/myst_stacks/demo.h b/engines/mohawk/myst_stacks/demo.h
index f19b9a6c2c..64a392502f 100644
--- a/engines/mohawk/myst_stacks/demo.h
+++ b/engines/mohawk/myst_stacks/demo.h
@@ -40,8 +40,8 @@ public:
Demo(MohawkEngine_Myst *vm);
~Demo();
- void disablePersistentScripts();
- void runPersistentScripts();
+ void disablePersistentScripts() override;
+ void runPersistentScripts() override;
private:
void setupOpcodes();
diff --git a/engines/mohawk/myst_stacks/dni.h b/engines/mohawk/myst_stacks/dni.h
index 3dc4645bd3..1a5f0911f9 100644
--- a/engines/mohawk/myst_stacks/dni.h
+++ b/engines/mohawk/myst_stacks/dni.h
@@ -40,12 +40,12 @@ public:
Dni(MohawkEngine_Myst *vm);
~Dni();
- void disablePersistentScripts();
- void runPersistentScripts();
+ void disablePersistentScripts() override;
+ void runPersistentScripts() override;
private:
void setupOpcodes();
- uint16 getVar(uint16 var);
+ uint16 getVar(uint16 var) override;
void atrus_run();
void loopVideo_run();
diff --git a/engines/mohawk/myst_stacks/intro.cpp b/engines/mohawk/myst_stacks/intro.cpp
index dc66984398..1d733d8100 100644
--- a/engines/mohawk/myst_stacks/intro.cpp
+++ b/engines/mohawk/myst_stacks/intro.cpp
@@ -170,7 +170,7 @@ void Intro::mystLinkBook_run() {
void Intro::o_mystLinkBook_init(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
debugC(kDebugScript, "Opcode %d: Myst link book init", op);
- _linkBookMovie = static_cast<MystResourceType6 *>(_invokingResource);
+ _linkBookMovie = getInvokingResource<MystAreaVideo>();
_startTime = 1;
_linkBookRunning = true;
}
diff --git a/engines/mohawk/myst_stacks/intro.h b/engines/mohawk/myst_stacks/intro.h
index a6c4a594d2..0095706795 100644
--- a/engines/mohawk/myst_stacks/intro.h
+++ b/engines/mohawk/myst_stacks/intro.h
@@ -29,7 +29,7 @@
namespace Mohawk {
-class MystResourceType6;
+class MystAreaVideo;
struct MystScriptEntry;
namespace MystStacks {
@@ -41,12 +41,12 @@ public:
Intro(MohawkEngine_Myst *vm);
~Intro();
- void disablePersistentScripts();
- void runPersistentScripts();
+ void disablePersistentScripts() override;
+ void runPersistentScripts() override;
private:
void setupOpcodes();
- uint16 getVar(uint16 var);
+ uint16 getVar(uint16 var) override;
DECLARE_OPCODE(o_useLinkBook);
@@ -60,7 +60,7 @@ private:
uint16 _introStep;
bool _linkBookRunning;
- MystResourceType6 *_linkBookMovie;
+ MystAreaVideo *_linkBookMovie;
};
} // End of namespace MystStacks
diff --git a/engines/mohawk/myst_stacks/makingof.h b/engines/mohawk/myst_stacks/makingof.h
index 79ef913bcf..41f91bc3fa 100644
--- a/engines/mohawk/myst_stacks/makingof.h
+++ b/engines/mohawk/myst_stacks/makingof.h
@@ -40,8 +40,8 @@ public:
MakingOf(MohawkEngine_Myst *vm);
~MakingOf();
- void disablePersistentScripts();
- void runPersistentScripts();
+ void disablePersistentScripts() override;
+ void runPersistentScripts() override;
private:
void setupOpcodes();
diff --git a/engines/mohawk/myst_stacks/mechanical.cpp b/engines/mohawk/myst_stacks/mechanical.cpp
index ffcaa226c6..3324c9a22d 100644
--- a/engines/mohawk/myst_stacks/mechanical.cpp
+++ b/engines/mohawk/myst_stacks/mechanical.cpp
@@ -40,6 +40,9 @@ Mechanical::Mechanical(MohawkEngine_Myst *vm) :
setupOpcodes();
_elevatorGoingMiddle = false;
+ _elevatorPosition = 0;
+
+ _crystalLit = 0;
_mystStaircaseState = false;
_fortressPosition = 0;
@@ -277,7 +280,7 @@ void Mechanical::o_throneEnablePassage(uint16 op, uint16 var, uint16 argc, uint1
void Mechanical::o_birdCrankStart(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
debugC(kDebugScript, "Opcode %d: Mechanical bird crank start", op);
- MystResourceType11 *crank = static_cast<MystResourceType11 *>(_invokingResource);
+ MystAreaDrag *crank = getInvokingResource<MystAreaDrag>();
uint16 crankSoundId = crank->getList2(0);
_vm->_sound->replaceSoundMyst(crankSoundId, Audio::Mixer::kMaxChannelVolume, true);
@@ -285,16 +288,16 @@ void Mechanical::o_birdCrankStart(uint16 op, uint16 var, uint16 argc, uint16 *ar
_birdSingEndTime = 0;
_birdCrankStartTime = _vm->_system->getMillis();
- MystResourceType6 *crankMovie = static_cast<MystResourceType6 *>(crank->getSubResource(0));
+ MystAreaVideo *crankMovie = static_cast<MystAreaVideo *>(crank->getSubResource(0));
crankMovie->playMovie();
}
void Mechanical::o_birdCrankStop(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
debugC(kDebugScript, "Opcode %d: Mechanical bird crank stop", op);
- MystResourceType11 *crank = static_cast<MystResourceType11 *>(_invokingResource);
+ MystAreaDrag *crank = getInvokingResource<MystAreaDrag>();
- MystResourceType6 *crankMovie = static_cast<MystResourceType6 *>(crank->getSubResource(0));
+ MystAreaVideo *crankMovie = static_cast<MystAreaVideo *>(crank->getSubResource(0));
crankMovie->pauseMovie(true);
uint16 crankSoundId = crank->getList2(1);
@@ -334,7 +337,7 @@ void Mechanical::o_fortressStaircaseMovie(uint16 op, uint16 var, uint16 argc, ui
void Mechanical::o_elevatorRotationStart(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
debugC(kDebugScript, "Opcode %d: Elevator rotation lever start", op);
- MystResourceType12 *lever = static_cast<MystResourceType12 *>(_invokingResource);
+ MystVideoInfo *lever = getInvokingResource<MystVideoInfo>();
lever->drawFrame(0);
_elevatorRotationLeverMoving = true;
@@ -349,7 +352,7 @@ void Mechanical::o_elevatorRotationMove(uint16 op, uint16 var, uint16 argc, uint
debugC(kDebugScript, "Opcode %d: Elevator rotation lever move", op);
const Common::Point &mouse = _vm->_system->getEventManager()->getMousePos();
- MystResourceType12 *lever = static_cast<MystResourceType12 *>(_invokingResource);
+ MystVideoInfo *lever = getInvokingResource<MystVideoInfo>();
// Make the handle follow the mouse
int16 maxStep = lever->getNumFrames() - 1;
@@ -367,7 +370,7 @@ void Mechanical::o_elevatorRotationStop(uint16 op, uint16 var, uint16 argc, uint
debugC(kDebugScript, "Opcode %d: Elevator rotation lever stop", op);
const Common::Point &mouse = _vm->_system->getEventManager()->getMousePos();
- MystResourceType12 *lever = static_cast<MystResourceType12 *>(_invokingResource);
+ MystVideoInfo *lever = getInvokingResource<MystVideoInfo>();
// Get current lever frame
int16 maxStep = lever->getNumFrames() - 1;
@@ -416,7 +419,7 @@ void Mechanical::o_fortressRotationSpeedStart(uint16 op, uint16 var, uint16 argc
_vm->_cursor->setCursor(700);
- MystResourceType12 *lever = static_cast<MystResourceType12 *>(_invokingResource);
+ MystVideoInfo *lever = getInvokingResource<MystVideoInfo>();
lever->drawFrame(0);
}
@@ -424,7 +427,7 @@ void Mechanical::o_fortressRotationSpeedMove(uint16 op, uint16 var, uint16 argc,
debugC(kDebugScript, "Opcode %d Fortress rotation speed lever move", op);
const Common::Point &mouse = _vm->_system->getEventManager()->getMousePos();
- MystResourceType12 *lever = static_cast<MystResourceType12 *>(_invokingResource);
+ MystVideoInfo *lever = getInvokingResource<MystVideoInfo>();
// Make the handle follow the mouse
int16 maxStep = lever->getNumFrames() - 1;
@@ -441,7 +444,7 @@ void Mechanical::o_fortressRotationSpeedMove(uint16 op, uint16 var, uint16 argc,
void Mechanical::o_fortressRotationSpeedStop(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
debugC(kDebugScript, "Opcode %d Fortress rotation speed lever stop", op);
- MystResourceType12 *lever = static_cast<MystResourceType12 *>(_invokingResource);
+ MystVideoInfo *lever = getInvokingResource<MystVideoInfo>();
// Release lever
for (int i = _fortressRotationSpeed; i >= 0; i--) {
@@ -459,7 +462,7 @@ void Mechanical::o_fortressRotationBrakeStart(uint16 op, uint16 var, uint16 argc
_vm->_cursor->setCursor(700);
- MystResourceType12 *lever = static_cast<MystResourceType12 *>(_invokingResource);
+ MystVideoInfo *lever = getInvokingResource<MystVideoInfo>();
lever->drawFrame(_fortressRotationBrake);
}
@@ -467,7 +470,7 @@ void Mechanical::o_fortressRotationBrakeMove(uint16 op, uint16 var, uint16 argc,
debugC(kDebugScript, "Opcode %d Fortress rotation brake lever move", op);
const Common::Point &mouse = _vm->_system->getEventManager()->getMousePos();
- MystResourceType12 *lever = static_cast<MystResourceType12 *>(_invokingResource);
+ MystVideoInfo *lever = getInvokingResource<MystVideoInfo>();
// Make the handle follow the mouse
int16 maxStep = lever->getNumFrames() - 1;
@@ -484,7 +487,7 @@ void Mechanical::o_fortressRotationBrakeMove(uint16 op, uint16 var, uint16 argc,
void Mechanical::o_fortressRotationBrakeStop(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
debugC(kDebugScript, "Opcode %d Fortress rotation brake lever stop", op);
- MystResourceType12 *lever = static_cast<MystResourceType12 *>(_invokingResource);
+ MystVideoInfo *lever = getInvokingResource<MystVideoInfo>();
lever->drawFrame(_fortressRotationBrake);
_vm->checkCursorHints();
@@ -495,7 +498,7 @@ void Mechanical::o_fortressSimulationSpeedStart(uint16 op, uint16 var, uint16 ar
_vm->_cursor->setCursor(700);
- MystResourceType12 *lever = static_cast<MystResourceType12 *>(_invokingResource);
+ MystVideoInfo *lever = getInvokingResource<MystVideoInfo>();
lever->drawFrame(0);
}
@@ -503,7 +506,7 @@ void Mechanical::o_fortressSimulationSpeedMove(uint16 op, uint16 var, uint16 arg
debugC(kDebugScript, "Opcode %d Fortress rotation simulator speed lever move", op);
const Common::Point &mouse = _vm->_system->getEventManager()->getMousePos();
- MystResourceType12 *lever = static_cast<MystResourceType12 *>(_invokingResource);
+ MystVideoInfo *lever = getInvokingResource<MystVideoInfo>();
// Make the handle follow the mouse
int16 maxStep = lever->getNumFrames() - 1;
@@ -520,7 +523,7 @@ void Mechanical::o_fortressSimulationSpeedMove(uint16 op, uint16 var, uint16 arg
void Mechanical::o_fortressSimulationSpeedStop(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
debugC(kDebugScript, "Opcode %d Fortress rotation simulator speed lever stop", op);
- MystResourceType12 *lever = static_cast<MystResourceType12 *>(_invokingResource);
+ MystVideoInfo *lever = getInvokingResource<MystVideoInfo>();
// Release lever
for (int i = _fortressSimulationSpeed; i >= 0; i--) {
@@ -538,7 +541,7 @@ void Mechanical::o_fortressSimulationBrakeStart(uint16 op, uint16 var, uint16 ar
_vm->_cursor->setCursor(700);
- MystResourceType12 *lever = static_cast<MystResourceType12 *>(_invokingResource);
+ MystVideoInfo *lever = getInvokingResource<MystVideoInfo>();
lever->drawFrame(_fortressSimulationBrake);
}
@@ -546,7 +549,7 @@ void Mechanical::o_fortressSimulationBrakeMove(uint16 op, uint16 var, uint16 arg
debugC(kDebugScript, "Opcode %d Fortress rotation simulator brake lever move", op);
const Common::Point &mouse = _vm->_system->getEventManager()->getMousePos();
- MystResourceType12 *lever = static_cast<MystResourceType12 *>(_invokingResource);
+ MystVideoInfo *lever = getInvokingResource<MystVideoInfo>();
// Make the handle follow the mouse
int16 maxStep = lever->getNumFrames() - 1;
@@ -563,7 +566,7 @@ void Mechanical::o_fortressSimulationBrakeMove(uint16 op, uint16 var, uint16 arg
void Mechanical::o_fortressSimulationBrakeStop(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
debugC(kDebugScript, "Opcode %d Fortress rotation simulator brake lever stop", op);
- MystResourceType12 *lever = static_cast<MystResourceType12 *>(_invokingResource);
+ MystVideoInfo *lever = getInvokingResource<MystVideoInfo>();
lever->drawFrame(_fortressSimulationBrake);
_vm->checkCursorHints();
@@ -664,7 +667,7 @@ void Mechanical::o_elevatorTopMovie(uint16 op, uint16 var, uint16 argc, uint16 *
void Mechanical::o_fortressRotationSetPosition(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
debugC(kDebugScript, "Opcode %d: Set fortress position", op);
- VideoHandle gears = _fortressRotationGears->playMovie();
+ VideoHandle gears = _fortressRotationGears->getMovieHandle();
uint32 moviePosition = Audio::Timestamp(gears->getTime(), 600).totalNumberOfFrames();
// Myst ME short movie workaround, explained in o_fortressRotation_init
@@ -737,7 +740,7 @@ void Mechanical::o_throne_init(uint16 op, uint16 var, uint16 argc, uint16 *argv)
// Used on Card 6238 (Sirrus' Throne) and Card 6027 (Achenar's Throne)
debugC(kDebugScript, "Opcode %d: Brother throne init", op);
- _invokingResource->setEnabled(getVar(var));
+ getInvokingResource<MystArea>()->setEnabled(getVar(var));
}
void Mechanical::o_fortressStaircase_init(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
@@ -763,13 +766,13 @@ void Mechanical::o_bird_init(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
_birdSinging = false;
_birdSingEndTime = 0;
- _bird = static_cast<MystResourceType6 *>(_invokingResource);
+ _bird = getInvokingResource<MystAreaVideo>();
}
void Mechanical::o_snakeBox_init(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
debugC(kDebugScript, "Opcode %d: Snake box init", op);
- _snakeBox = static_cast<MystResourceType6 *>(_invokingResource);
+ _snakeBox = getInvokingResource<MystAreaVideo>();
}
void Mechanical::elevatorRotation_run() {
@@ -798,7 +801,7 @@ void Mechanical::o_elevatorRotation_init(uint16 op, uint16 var, uint16 argc, uin
}
void Mechanical::fortressRotation_run() {
- VideoHandle gears = _fortressRotationGears->playMovie();
+ VideoHandle gears = _fortressRotationGears->getMovieHandle();
double oldRate = gears->getRate().toDouble();
uint32 moviePosition = Audio::Timestamp(gears->getTime(), 600).totalNumberOfFrames();
@@ -872,7 +875,7 @@ void Mechanical::fortressRotation_run() {
void Mechanical::o_fortressRotation_init(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
debugC(kDebugScript, "Opcode %d: Fortress rotation init", op);
- _fortressRotationGears = static_cast<MystResourceType6 *>(_invokingResource);
+ _fortressRotationGears = getInvokingResource<MystAreaVideo>();
VideoHandle gears = _fortressRotationGears->playMovie();
gears->setLooping(true);
@@ -938,13 +941,22 @@ void Mechanical::fortressSimulation_run() {
holo->setLooping(true);
holo->setRate(0);
+ // HACK: Support negative rates with edit lists
+ _fortressSimulationHoloRate = 0;
+ // END HACK
+
_vm->_cursor->showCursor();
_fortressSimulationInit = false;
} else {
- VideoHandle holo = _fortressSimulationHolo->playMovie();
+ VideoHandle holo = _fortressSimulationHolo->getMovieHandle();
double oldRate = holo->getRate().toDouble();
+
+ // HACK: Support negative rates with edit lists
+ oldRate = _fortressSimulationHoloRate;
+ // END HACK
+
uint32 moviePosition = Audio::Timestamp(holo->getTime(), 600).totalNumberOfFrames();
int32 positionInQuarter = 900 - (moviePosition + 900) % 1800;
@@ -978,7 +990,26 @@ void Mechanical::fortressSimulation_run() {
newRate = CLIP<double>(newRate, -2.5, 2.5);
- holo->setRate(Common::Rational((int)(newRate * 1000.0), 1000));
+ // HACK: Support negative rates with edit lists
+
+ // Our current QuickTime implementation does not support negative
+ // playback rates for movies using edit lists.
+ // The fortress rotation simulator movie this code handles is the
+ // only movie in the game requiring that feature.
+
+ // This hack approximates the next frame to display when the rate
+ // is negative, and seeks to it. It's not intended to be precise.
+
+ _fortressSimulationHoloRate = newRate;
+
+ if (_fortressSimulationHoloRate < 0) {
+ double newMoviePosition = moviePosition + _fortressSimulationHoloRate * 10;
+ holo->setRate(0);
+ holo->seek(Audio::Timestamp(0, (uint)newMoviePosition, 600));
+ } else {
+ holo->setRate(Common::Rational((int)(newRate * 1000.0), 1000));
+ }
+ // END HACK
_gearsWereRunning = true;
} else if (_gearsWereRunning) {
@@ -986,6 +1017,11 @@ void Mechanical::fortressSimulation_run() {
uint16 simulationPosition = (moviePosition + 900) / 1800 % 4;
holo->setRate(0);
+
+ // HACK: Support negative rates with edit lists
+ _fortressSimulationHoloRate = 0;
+ // END HACK
+
holo->seek(Audio::Timestamp(0, 1800 * simulationPosition, 600));
_vm->_sound->playSoundBlocking( _fortressRotationSounds[simulationPosition]);
@@ -997,7 +1033,7 @@ void Mechanical::fortressSimulation_run() {
void Mechanical::o_fortressSimulation_init(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
debugC(kDebugScript, "Opcode %d: Fortress rotation simulator init", op);
- _fortressSimulationHolo = static_cast<MystResourceType6 *>(_invokingResource);
+ _fortressSimulationHolo = getInvokingResource<MystAreaVideo>();
_fortressSimulationStartSound1 = argv[0];
_fortressSimulationStartSound2 = argv[1];
@@ -1019,7 +1055,7 @@ void Mechanical::o_fortressSimulation_init(uint16 op, uint16 var, uint16 argc, u
void Mechanical::o_fortressSimulationStartup_init(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
debugC(kDebugScript, "Opcode %d: Fortress rotation simulator startup init", op);
- _fortressSimulationStartup = static_cast<MystResourceType6 *>(_invokingResource);
+ _fortressSimulationStartup = getInvokingResource<MystAreaVideo>();
}
} // End of namespace MystStacks
diff --git a/engines/mohawk/myst_stacks/mechanical.h b/engines/mohawk/myst_stacks/mechanical.h
index 6360b2be2d..aae02df433 100644
--- a/engines/mohawk/myst_stacks/mechanical.h
+++ b/engines/mohawk/myst_stacks/mechanical.h
@@ -40,16 +40,16 @@ public:
Mechanical(MohawkEngine_Myst *vm);
~Mechanical();
- void disablePersistentScripts();
- void runPersistentScripts();
+ void disablePersistentScripts() override;
+ void runPersistentScripts() override;
private:
void setupOpcodes();
- uint16 getVar(uint16 var);
- void toggleVar(uint16 var);
- bool setVarValue(uint16 var, uint16 value);
+ uint16 getVar(uint16 var) override;
+ void toggleVar(uint16 var) override;
+ bool setVarValue(uint16 var, uint16 value) override;
- virtual uint16 getMap() { return 9931; }
+ virtual uint16 getMap() override { return 9931; }
void birdSing_run();
void elevatorRotation_run();
@@ -109,7 +109,7 @@ private:
uint16 _fortressRotationBrake; // 80
uint16 _fortressPosition; // 82
uint16 _fortressRotationSounds[4]; // 86 to 92
- MystResourceType6 *_fortressRotationGears; // 172
+ MystAreaVideo *_fortressRotationGears; // 172
bool _fortressRotationShortMovieWorkaround;
uint32 _fortressRotationShortMovieCount;
@@ -121,8 +121,12 @@ private:
uint16 _fortressSimulationBrake; // 98
uint16 _fortressSimulationStartSound1; // 102
uint16 _fortressSimulationStartSound2; // 100
- MystResourceType6 *_fortressSimulationHolo; // 160
- MystResourceType6 *_fortressSimulationStartup; // 164
+ MystAreaVideo *_fortressSimulationHolo; // 160
+ MystAreaVideo *_fortressSimulationStartup; // 164
+
+ // HACK: Support negative rates with edit lists
+ double _fortressSimulationHoloRate;
+ // END HACK
uint16 _elevatorGoingDown; // 112
@@ -143,10 +147,10 @@ private:
bool _birdSinging; // 144
uint32 _birdCrankStartTime; // 136
uint32 _birdSingEndTime; // 140
- MystResourceType6 *_bird; // 152
+ MystAreaVideo *_bird; // 152
- MystResourceType6 *_snakeBox; // 156
+ MystAreaVideo *_snakeBox; // 156
};
} // End of namespace MystStacks
diff --git a/engines/mohawk/myst_stacks/myst.cpp b/engines/mohawk/myst_stacks/myst.cpp
index 9ad1635757..9d23d2fb10 100644
--- a/engines/mohawk/myst_stacks/myst.cpp
+++ b/engines/mohawk/myst_stacks/myst.cpp
@@ -50,14 +50,16 @@ Myst::Myst(MohawkEngine_Myst *vm) :
_libraryBookcaseChanged = false;
_dockVaultState = 0;
_cabinDoorOpened = 0;
+ _cabinHandleDown = 0;
_cabinMatchState = 2;
+ _cabinGaugeMovieEnabled = false;
_matchBurning = false;
- _tree = 0;
- _treeAlcove = 0;
+ _tree = nullptr;
+ _treeAlcove = nullptr;
_treeStopped = false;
_treeMinPosition = 0;
_imagerValidationStep = 0;
- _observatoryCurrentSlider = 0;
+ _observatoryCurrentSlider = nullptr;
_butterfliesMoviePlayed = false;
_state.treeLastMoveTime = _vm->_system->getMillis();
}
@@ -626,7 +628,7 @@ uint16 Myst::getVar(uint16 var) {
case 307: // Cabin Boiler Fully Pressurized
return _state.cabinPilotLightLit == 1 && _state.cabinValvePosition > 12;
case 308: // Cabin handle position
- return 0; // Not implemented in the original
+ return _cabinHandleDown;
default:
return MystScriptParser::getVar(var);
}
@@ -764,6 +766,9 @@ bool Myst::setVarValue(uint16 var, uint16 value) {
case 304: // Myst Library Image Present on Tower Rotation Map
_towerRotationMapInitialized = value;
break;
+ case 308: // Cabin handle position
+ _cabinHandleDown = value;
+ break;
case 309: // Tree stopped
_treeStopped = value;
break;
@@ -860,14 +865,14 @@ void Myst::o_fireplaceToggleButton(uint16 op, uint16 var, uint16 argc, uint16 *a
if (line & bitmask) {
// Unset button
for (uint i = 4795; i >= 4779; i--) {
- _vm->_gfx->copyImageToScreen(i, _invokingResource->getRect());
+ _vm->_gfx->copyImageToScreen(i, getInvokingResource<MystArea>()->getRect());
_vm->_system->updateScreen();
}
_fireplaceLines[var - 17] &= ~bitmask;
} else {
// Set button
for (uint i = 4779; i <= 4795; i++) {
- _vm->_gfx->copyImageToScreen(i, _invokingResource->getRect());
+ _vm->_gfx->copyImageToScreen(i, getInvokingResource<MystArea>()->getRect());
_vm->_system->updateScreen();
}
_fireplaceLines[var - 17] |= bitmask;
@@ -1251,7 +1256,7 @@ void Myst::o_imagerPlayButton(uint16 op, uint16 var, uint16 argc, uint16 *argv)
void Myst::o_imagerEraseButton(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
debugC(kDebugScript, "Opcode %d: Imager erase button", op);
- _imagerRedButton = static_cast<MystResourceType8 *>(_invokingResource->_parent);
+ _imagerRedButton = static_cast<MystAreaImageSwitch *>(getInvokingResource<MystArea>()->_parent);
for (uint i = 0; i < 4; i++)
_imagerSound[i] = argv[i];
_imagerValidationCard = argv[4];
@@ -1351,7 +1356,7 @@ void Myst::o_towerElevatorAnimation(uint16 op, uint16 var, uint16 argc, uint16 *
void Myst::o_generatorButtonPressed(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
debugC(kDebugScript, "Opcode %d: Generator button pressed", op);
- MystResource *button = _invokingResource->_parent;
+ MystArea *button = getInvokingResource<MystArea>()->_parent;
generatorRedrawRocket();
@@ -1376,7 +1381,7 @@ void Myst::o_generatorButtonPressed(uint16 op, uint16 var, uint16 argc, uint16 *
if (_generatorVoltage)
_vm->_sound->replaceSoundMyst(6297);
else {
- _vm->_sound->replaceSoundMyst(7297); // TODO: Replace with play sound and replace background 4297
+ _vm->_sound->replaceSoundMyst(7297);
_vm->_sound->replaceBackgroundMyst(4297);
}
@@ -1385,7 +1390,7 @@ void Myst::o_generatorButtonPressed(uint16 op, uint16 var, uint16 argc, uint16 *
}
// Redraw button
- _vm->redrawArea(button->getType8Var());
+ _vm->redrawArea(button->getImageSwitchVar());
// Blow breaker
if (_state.generatorVoltage > 59)
@@ -1398,8 +1403,8 @@ void Myst::generatorRedrawRocket() {
_vm->redrawArea(97);
}
-void Myst::generatorButtonValue(MystResource *button, uint16 &mask, uint16 &value) {
- switch (button->getType8Var()) {
+void Myst::generatorButtonValue(MystArea *button, uint16 &mask, uint16 &value) {
+ switch (button->getImageSwitchVar()) {
case 52: // Generator Switch #1
mask = 1;
value = 10;
@@ -1466,7 +1471,7 @@ void Myst::o_cabinSafeHandleStartMove(uint16 op, uint16 var, uint16 argc, uint16
debugC(kDebugScript, "Opcode %d: Cabin safe handle start move", op);
// Used on Card 4100
- MystResourceType12 *handle = static_cast<MystResourceType12 *>(_invokingResource);
+ MystVideoInfo *handle = getInvokingResource<MystVideoInfo>();
handle->drawFrame(0);
_vm->_cursor->setCursor(700);
_tempVar = 0;
@@ -1476,7 +1481,7 @@ void Myst::o_cabinSafeHandleMove(uint16 op, uint16 var, uint16 argc, uint16 *arg
debugC(kDebugScript, "Opcode %d: Cabin safe handle move", op);
// Used on Card 4100
- MystResourceType12 *handle = static_cast<MystResourceType12 *>(_invokingResource);
+ MystVideoInfo *handle = getInvokingResource<MystVideoInfo>();
if (handle->pullLeverV()) {
// Sound not played yet
@@ -1506,7 +1511,7 @@ void Myst::o_cabinSafeHandleEndMove(uint16 op, uint16 var, uint16 argc, uint16 *
debugC(kDebugScript, "Opcode %d: Cabin safe handle end move", op);
// Used on Card 4100
- MystResourceType12 *handle = static_cast<MystResourceType12 *>(_invokingResource);
+ MystVideoInfo *handle = getInvokingResource<MystVideoInfo>();
handle->drawFrame(0);
_vm->checkCursorHints();
}
@@ -1804,7 +1809,7 @@ void Myst::o_observatoryTimeSliderMove(uint16 op, uint16 var, uint16 argc, uint1
void Myst::o_circuitBreakerStartMove(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
debugC(kDebugScript, "Opcode %d: Circuit breaker start move", op);
- MystResourceType12 *breaker = static_cast<MystResourceType12 *>(_invokingResource);
+ MystVideoInfo *breaker = getInvokingResource<MystVideoInfo>();
breaker->drawFrame(0);
_vm->_cursor->setCursor(700);
_tempVar = 0;
@@ -1813,7 +1818,7 @@ void Myst::o_circuitBreakerStartMove(uint16 op, uint16 var, uint16 argc, uint16
void Myst::o_circuitBreakerMove(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
debugC(kDebugScript, "Opcode %d: Circuit breaker move", op);
- MystResourceType12 *breaker = static_cast<MystResourceType12 *>(_invokingResource);
+ MystVideoInfo *breaker = getInvokingResource<MystVideoInfo>();
const Common::Point &mouse = _vm->_system->getEventManager()->getMousePos();
int16 maxStep = breaker->getStepsV() - 1;
@@ -1828,7 +1833,7 @@ void Myst::o_circuitBreakerMove(uint16 op, uint16 var, uint16 argc, uint16 *argv
// Breaker switched
if (step == maxStep) {
// Choose breaker
- if (breaker->getType8Var() == 93) {
+ if (breaker->getImageSwitchVar() == 93) {
// Voltage is still too high or not broken
if (_state.generatorVoltage > 59 || _state.generatorBreakers != 1) {
uint16 soundId = breaker->getList2(1);
@@ -1864,8 +1869,8 @@ void Myst::o_circuitBreakerMove(uint16 op, uint16 var, uint16 argc, uint16 *argv
void Myst::o_circuitBreakerEndMove(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
debugC(kDebugScript, "Opcode %d: Circuit breaker end move", op);
- MystResourceType12 *breaker = static_cast<MystResourceType12 *>(_invokingResource);
- _vm->redrawArea(breaker->getType8Var());
+ MystVideoInfo *breaker = getInvokingResource<MystVideoInfo>();
+ _vm->redrawArea(breaker->getImageSwitchVar());
_vm->checkCursorHints();
}
@@ -2138,7 +2143,7 @@ void Myst::tree_run() {
// Check if alcove is accessible
treeSetAlcoveAccessible();
- if (_cabinGaugeMovie) {
+ if (_cabinGaugeMovieEnabled) {
Common::Rational rate = boilerComputeGaugeRate(pressure, delay);
boilerResetGauge(rate);
}
@@ -2187,22 +2192,22 @@ void Myst::o_rocketSoundSliderEndMove(uint16 op, uint16 var, uint16 argc, uint16
if (_state.generatorVoltage == 59 && !_state.generatorBreakers && _rocketSliderSound)
_vm->_sound->stopSound();
- if (_invokingResource == _rocketSlider1)
+ if (getInvokingResource<MystArea>() == _rocketSlider1)
_state.rocketSliderPosition[0] = _rocketSlider1->_pos.y;
- else if (_invokingResource == _rocketSlider2)
+ else if (getInvokingResource<MystArea>() == _rocketSlider2)
_state.rocketSliderPosition[1] = _rocketSlider2->_pos.y;
- else if (_invokingResource == _rocketSlider3)
+ else if (getInvokingResource<MystArea>() == _rocketSlider3)
_state.rocketSliderPosition[2] = _rocketSlider3->_pos.y;
- else if (_invokingResource == _rocketSlider4)
+ else if (getInvokingResource<MystArea>() == _rocketSlider4)
_state.rocketSliderPosition[3] = _rocketSlider4->_pos.y;
- else if (_invokingResource == _rocketSlider5)
+ else if (getInvokingResource<MystArea>() == _rocketSlider5)
_state.rocketSliderPosition[4] = _rocketSlider5->_pos.y;
_vm->_sound->resumeBackgroundMyst();
}
void Myst::rocketSliderMove() {
- MystResourceType10 *slider = static_cast<MystResourceType10 *>(_invokingResource);
+ MystAreaSlider *slider = getInvokingResource<MystAreaSlider>();
if (_state.generatorVoltage == 59 && !_state.generatorBreakers) {
uint16 soundId = rocketSliderGetSound(slider->_pos.y);
@@ -2262,7 +2267,7 @@ void Myst::rocketCheckSolution() {
if (solved) {
// Reset lever position
- MystResourceType12 *lever = static_cast<MystResourceType12 *>(_invokingResource);
+ MystVideoInfo *lever = getInvokingResource<MystVideoInfo>();
lever->drawFrame(0);
// Book appearing
@@ -2299,17 +2304,17 @@ void Myst::rocketCheckSolution() {
void Myst::o_rocketPianoStart(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
debugC(kDebugScript, "Opcode %d: Rocket piano start move", op);
- MystResourceType11 *key = static_cast<MystResourceType11 *>(_invokingResource);
+ MystAreaDrag *key = getInvokingResource<MystAreaDrag>();
// What the hell??
- Common::Rect src = key->_subImages[1].rect;
- Common::Rect rect = key->_subImages[0].rect;
+ Common::Rect src = key->getSubImage(1).rect;
+ Common::Rect rect = key->getSubImage(0).rect;
Common::Rect dest = rect;
dest.top = 332 - rect.bottom;
dest.bottom = 332 - rect.top;
// Draw pressed piano key
- _vm->_gfx->copyImageSectionToScreen(key->_subImages[1].wdib, src, dest);
+ _vm->_gfx->copyImageSectionToScreen(key->getSubImage(1).wdib, src, dest);
_vm->_system->updateScreen();
// Play note
@@ -2326,29 +2331,29 @@ void Myst::o_rocketPianoMove(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
Common::Rect piano = Common::Rect(85, 123, 460, 270);
// Unpress previous key
- MystResourceType11 *key = static_cast<MystResourceType11 *>(_invokingResource);
+ MystAreaDrag *key = getInvokingResource<MystAreaDrag>();
- Common::Rect src = key->_subImages[0].rect;
+ Common::Rect src = key->getSubImage(0).rect;
Common::Rect dest = src;
dest.top = 332 - src.bottom;
dest.bottom = 332 - src.top;
// Draw unpressed piano key
- _vm->_gfx->copyImageSectionToScreen(key->_subImages[0].wdib, src, dest);
+ _vm->_gfx->copyImageSectionToScreen(key->getSubImage(0).wdib, src, dest);
if (piano.contains(mouse)) {
- MystResource *resource = _vm->updateCurrentResource();
- if (resource && resource->type == kMystDragArea) {
+ MystArea *resource = _vm->updateCurrentResource();
+ if (resource && resource->type == kMystAreaDrag) {
// Press new key
- key = static_cast<MystResourceType11 *>(resource);
- src = key->_subImages[1].rect;
- Common::Rect rect = key->_subImages[0].rect;
+ key = static_cast<MystAreaDrag *>(resource);
+ src = key->getSubImage(1).rect;
+ Common::Rect rect = key->getSubImage(0).rect;
dest = rect;
dest.top = 332 - rect.bottom;
dest.bottom = 332 - rect.top;
// Draw pressed piano key
- _vm->_gfx->copyImageSectionToScreen(key->_subImages[1].wdib, src, dest);
+ _vm->_gfx->copyImageSectionToScreen(key->getSubImage(1).wdib, src, dest);
// Play note
if (_state.generatorVoltage == 59 && !_state.generatorBreakers) {
@@ -2368,15 +2373,15 @@ void Myst::o_rocketPianoMove(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
void Myst::o_rocketPianoStop(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
debugC(kDebugScript, "Opcode %d: Rocket piano end move", op);
- MystResourceType8 *key = static_cast<MystResourceType8 *>(_invokingResource);
+ MystAreaImageSwitch *key = getInvokingResource<MystAreaImageSwitch>();
- Common::Rect &src = key->_subImages[0].rect;
+ Common::Rect src = key->getSubImage(0).rect;
Common::Rect dest = src;
dest.top = 332 - src.bottom;
dest.bottom = 332 - src.top;
// Draw unpressed piano key
- _vm->_gfx->copyImageSectionToScreen(key->_subImages[0].wdib, src, dest);
+ _vm->_gfx->copyImageSectionToScreen(key->getSubImage(0).wdib, src, dest);
_vm->_system->updateScreen();
_vm->_sound->stopSound();
@@ -2386,7 +2391,7 @@ void Myst::o_rocketPianoStop(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
void Myst::o_rocketLeverStartMove(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
debugC(kDebugScript, "Opcode %d: Rocket lever start move", op);
- MystResourceType12 *lever = static_cast<MystResourceType12 *>(_invokingResource);
+ MystVideoInfo *lever = getInvokingResource<MystVideoInfo>();
_vm->_cursor->setCursor(700);
_rocketLeverPosition = 0;
@@ -2406,7 +2411,7 @@ void Myst::o_rocketOpenBook(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
void Myst::o_rocketLeverMove(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
debugC(kDebugScript, "Opcode %d: Rocket lever move", op);
- MystResourceType12 *lever = static_cast<MystResourceType12 *>(_invokingResource);
+ MystVideoInfo *lever = getInvokingResource<MystVideoInfo>();
const Common::Point &mouse = _vm->_system->getEventManager()->getMousePos();
// Make the lever follow the mouse
@@ -2435,7 +2440,7 @@ void Myst::o_rocketLeverMove(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
void Myst::o_rocketLeverEndMove(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
debugC(kDebugScript, "Opcode %d: Rocket lever end move", op);
- MystResourceType12 *lever = static_cast<MystResourceType12 *>(_invokingResource);
+ MystVideoInfo *lever = getInvokingResource<MystVideoInfo>();
_vm->checkCursorHints();
_rocketLeverPosition = 0;
@@ -2712,7 +2717,7 @@ void Myst::clockWheel_run() {
}
void Myst::clockWheelStartTurn(uint16 wheel) {
- MystResourceType11 *resource = static_cast<MystResourceType11 *>(_invokingResource);
+ MystAreaDrag *resource = getInvokingResource<MystAreaDrag>();
uint16 soundId = resource->getList1(0);
if (soundId)
@@ -2835,10 +2840,10 @@ void Myst::o_observatoryChangeSettingStop(uint16 op, uint16 var, uint16 argc, ui
_observatoryIncrement = 0;
// Restore button and slider
- _vm->_gfx->copyBackBufferToScreen(_invokingResource->getRect());
+ _vm->_gfx->copyBackBufferToScreen(getInvokingResource<MystArea>()->getRect());
if (_observatoryCurrentSlider) {
_vm->redrawResource(_observatoryCurrentSlider);
- _observatoryCurrentSlider = 0;
+ _observatoryCurrentSlider = nullptr;
}
_vm->_sound->resumeBackgroundMyst();
}
@@ -2874,7 +2879,7 @@ void Myst::o_imagerEraseStop(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
void Myst::o_clockLeverStartMove(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
debugC(kDebugScript, "Opcode %d: Clock lever start move", op);
- MystResourceType12 *lever = static_cast<MystResourceType12 *>(_invokingResource);
+ MystVideoInfo *lever = getInvokingResource<MystVideoInfo>();
lever->drawFrame(0);
_vm->_cursor->setCursor(700);
_clockMiddleGearMovedAlone = false;
@@ -2885,7 +2890,7 @@ void Myst::o_clockLeverMove(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
debugC(kDebugScript, "Opcode %d: Clock left lever move", op);
if (!_clockLeverPulled) {
- MystResourceType12 *lever = static_cast<MystResourceType12 *>(_invokingResource);
+ MystVideoInfo *lever = getInvokingResource<MystVideoInfo>();
// If lever pulled
if (lever->pullLeverV()) {
@@ -2977,7 +2982,7 @@ void Myst::o_clockLeverEndMove(uint16 op, uint16 var, uint16 argc, uint16 *argv)
_vm->_sound->replaceSoundMyst(8113);
// Release lever
- MystResourceType12 *lever = static_cast<MystResourceType12 *>(_invokingResource);
+ MystVideoInfo *lever = getInvokingResource<MystVideoInfo>();
lever->releaseLeverV();
// Check if puzzle is solved
@@ -3021,7 +3026,7 @@ void Myst::clockGearsCheckSolution() {
void Myst::o_clockResetLeverStartMove(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
debugC(kDebugScript, "Opcode %d: Clock reset lever start move", op);
- MystResourceType12 *lever = static_cast<MystResourceType12 *>(_invokingResource);
+ MystVideoInfo *lever = getInvokingResource<MystVideoInfo>();
lever->drawFrame(0);
_vm->_cursor->setCursor(700);
}
@@ -3029,7 +3034,7 @@ void Myst::o_clockResetLeverStartMove(uint16 op, uint16 var, uint16 argc, uint16
void Myst::o_clockResetLeverMove(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
debugC(kDebugScript, "Opcode %d: Clock reset lever move", op);
- MystResourceType12 *lever = static_cast<MystResourceType12 *>(_invokingResource);
+ MystVideoInfo *lever = getInvokingResource<MystVideoInfo>();
// If pulled
if (lever->pullLeverV() && _clockWeightPosition != 0)
@@ -3125,7 +3130,7 @@ void Myst::o_clockResetLeverEndMove(uint16 op, uint16 var, uint16 argc, uint16 *
debugC(kDebugScript, "Opcode %d: Clock reset lever end move", op);
// Get current lever frame
- MystResourceType12 *lever = static_cast<MystResourceType12 *>(_invokingResource);
+ MystVideoInfo *lever = getInvokingResource<MystVideoInfo>();
lever->releaseLeverV();
@@ -3189,8 +3194,8 @@ void Myst::towerRotationMap_run() {
void Myst::o_towerRotationMap_init(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
_towerRotationMapRunning = true;
- _towerRotationMapTower = static_cast<MystResourceType11 *>(_invokingResource);
- _towerRotationMapLabel = static_cast<MystResourceType8 *>(_vm->_resources[argv[0]]);
+ _towerRotationMapTower = getInvokingResource<MystAreaImageSwitch>();
+ _towerRotationMapLabel = _vm->getViewResource<MystAreaImageSwitch>(argv[0]);
_tempVar = 0;
_startTime = 0;
_towerRotationMapClicked = false;
@@ -3202,7 +3207,7 @@ void Myst::towerRotationDrawBuildings() {
// Draw other resources
for (uint i = 1; i <= 10; i++) {
- MystResourceType8 *resource = static_cast<MystResourceType8 *>(_vm->_resources[i]);
+ MystAreaImageSwitch *resource = _vm->getViewResource<MystAreaImageSwitch>(i);
_vm->redrawResource(resource, false);
}
}
@@ -3313,7 +3318,7 @@ void Myst::o_forechamberDoor_init(uint16 op, uint16 var, uint16 argc, uint16 *ar
void Myst::o_shipAccess_init(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
// Enable acces to the ship
if (_state.shipFloating) {
- _invokingResource->setEnabled(true);
+ getInvokingResource<MystArea>()->setEnabled(true);
}
}
@@ -3322,7 +3327,7 @@ void Myst::o_butterflies_init(uint16 op, uint16 var, uint16 argc, uint16 *argv)
// Used for Card 4256 (Butterfly Movie Activation)
if (!_butterfliesMoviePlayed) {
- MystResourceType6 *butterflies = static_cast<MystResourceType6 *>(_invokingResource);
+ MystAreaVideo *butterflies = getInvokingResource<MystAreaVideo>();
butterflies->playMovie();
_butterfliesMoviePlayed = true;
@@ -3333,8 +3338,8 @@ void Myst::o_imager_init(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
debugC(kDebugScript, "Opcode %d: Imager init", op);
debugC(kDebugScript, "Var: %d", var);
- MystResourceType7 *select = static_cast<MystResourceType7 *>(_invokingResource);
- _imagerMovie = static_cast<MystResourceType6 *>(select->getSubResource(getVar(var)));
+ MystAreaActionSwitch *select = getInvokingResource<MystAreaActionSwitch>();
+ _imagerMovie = static_cast<MystAreaVideo *>(select->getSubResource(getVar(var)));
_imagerRunning = true;
}
@@ -3377,8 +3382,8 @@ void Myst::libraryBookcaseTransform_run(void) {
void Myst::o_libraryBookcaseTransform_init(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
if (_libraryBookcaseChanged) {
- MystResourceType7 *resource = static_cast<MystResourceType7 *>(_invokingResource);
- _libraryBookcaseMovie = static_cast<MystResourceType6 *>(resource->getSubResource(getVar(0)));
+ MystAreaActionSwitch *resource = getInvokingResource<MystAreaActionSwitch>();
+ _libraryBookcaseMovie = static_cast<MystAreaVideo *>(resource->getSubResource(getVar(0)));
_libraryBookcaseSoundId = argv[0];
_libraryBookcaseMoving = true;
}
@@ -3471,17 +3476,17 @@ void Myst::o_observatory_init(uint16 op, uint16 var, uint16 argc, uint16 *argv)
_tempVar = 0;
_observatoryNotInitialized = true;
- _observatoryVisualizer = static_cast<MystResourceType8 *>(_invokingResource);
- _observatoryGoButton = static_cast<MystResourceType8 *>(_vm->_resources[argv[0]]);
+ _observatoryVisualizer = getInvokingResource<MystAreaImageSwitch>();
+ _observatoryGoButton = _vm->getViewResource<MystAreaImageSwitch>(argv[0]);
if (observatoryIsDDMMYYYY2400()) {
- _observatoryDaySlider = static_cast<MystResourceType10 *>(_vm->_resources[argv[1]]);
- _observatoryMonthSlider = static_cast<MystResourceType10 *>(_vm->_resources[argv[2]]);
+ _observatoryDaySlider = _vm->getViewResource<MystAreaSlider>(argv[1]);
+ _observatoryMonthSlider = _vm->getViewResource<MystAreaSlider>(argv[2]);
} else {
- _observatoryMonthSlider = static_cast<MystResourceType10 *>(_vm->_resources[argv[1]]);
- _observatoryDaySlider = static_cast<MystResourceType10 *>(_vm->_resources[argv[2]]);
+ _observatoryMonthSlider = _vm->getViewResource<MystAreaSlider>(argv[1]);
+ _observatoryDaySlider = _vm->getViewResource<MystAreaSlider>(argv[2]);
}
- _observatoryYearSlider = static_cast<MystResourceType10 *>(_vm->_resources[argv[3]]);
- _observatoryTimeSlider = static_cast<MystResourceType10 *>(_vm->_resources[argv[4]]);
+ _observatoryYearSlider = _vm->getViewResource<MystAreaSlider>(argv[3]);
+ _observatoryTimeSlider = _vm->getViewResource<MystAreaSlider>(argv[4]);
// Set date selection sliders position
_observatoryDaySlider->setPosition(_state.observatoryDaySlider);
@@ -3503,18 +3508,14 @@ bool Myst::observatoryIsDDMMYYYY2400() {
}
void Myst::observatoryUpdateVisualizer(uint16 x, uint16 y) {
- Common::Rect &visu0 = _observatoryVisualizer->_subImages[0].rect;
- Common::Rect &visu1 = _observatoryVisualizer->_subImages[1].rect;
-
- visu0.left = x;
- visu0.right = visu0.left + 105;
- visu0.bottom = 512 - y;
- visu0.top = visu0.bottom - 106;
+ Common::Rect visu;
+ visu.left = x;
+ visu.right = visu.left + 105;
+ visu.bottom = 512 - y;
+ visu.top = visu.bottom - 106;
- visu1.left = visu0.left;
- visu1.top = visu0.top;
- visu1.right = visu0.right;
- visu1.bottom = visu0.bottom;
+ _observatoryVisualizer->setSubImageRect(0, visu);
+ _observatoryVisualizer->setSubImageRect(1, visu);
}
void Myst::observatorySetTargetToSetting() {
@@ -3618,13 +3619,13 @@ void Myst::gullsFly2_run() {
void Myst::o_treeCard_init(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
debugC(kDebugScript, "Opcode %d: Enter tree card", op);
- _tree = static_cast<MystResourceType8 *>(_invokingResource);
+ _tree = getInvokingResource<MystAreaImageSwitch>();
}
void Myst::o_treeEntry_init(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
debugC(kDebugScript, "Opcode %d: Enter tree card with entry", op);
- _treeAlcove = static_cast<MystResourceType5 *>(_invokingResource);
+ _treeAlcove = getInvokingResource<MystArea>();
_treeMinAccessiblePosition = argv[0];
_treeMaxAccessiblePosition = argv[1];
@@ -3703,16 +3704,18 @@ void Myst::boilerGaugeInit() {
frame = Audio::Timestamp(0, 0, 600);
_vm->_video->drawVideoFrame(_cabinGaugeMovie, frame);
+
+ _cabinGaugeMovieEnabled = true;
}
void Myst::o_rocketSliders_init(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
debugC(kDebugScript, "Opcode %d: Rocket sliders init", op);
- _rocketSlider1 = static_cast<MystResourceType10 *>(_vm->_resources[argv[0]]);
- _rocketSlider2 = static_cast<MystResourceType10 *>(_vm->_resources[argv[1]]);
- _rocketSlider3 = static_cast<MystResourceType10 *>(_vm->_resources[argv[2]]);
- _rocketSlider4 = static_cast<MystResourceType10 *>(_vm->_resources[argv[3]]);
- _rocketSlider5 = static_cast<MystResourceType10 *>(_vm->_resources[argv[4]]);
+ _rocketSlider1 = _vm->getViewResource<MystAreaSlider>(argv[0]);
+ _rocketSlider2 = _vm->getViewResource<MystAreaSlider>(argv[1]);
+ _rocketSlider3 = _vm->getViewResource<MystAreaSlider>(argv[2]);
+ _rocketSlider4 = _vm->getViewResource<MystAreaSlider>(argv[3]);
+ _rocketSlider5 = _vm->getViewResource<MystAreaSlider>(argv[4]);
// Initialize sliders position
for (uint i = 0; i < 5; i++)
@@ -3828,13 +3831,13 @@ void Myst::o_bookAddSpecialPage_exit(uint16 op, uint16 var, uint16 argc, uint16
void Myst::o_treeCard_exit(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
debugC(kDebugScript, "Opcode %d: Exit tree card", op);
- _tree = 0;
+ _tree = nullptr;
}
void Myst::o_treeEntry_exit(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
debugC(kDebugScript, "Opcode %d: Exit tree card with entry", op);
- _treeAlcove = 0;
+ _treeAlcove = nullptr;
}
void Myst::o_boiler_exit(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
@@ -3842,6 +3845,8 @@ void Myst::o_boiler_exit(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
_cabinGaugeMovie = VideoHandle();
_cabinFireMovie = VideoHandle();
+
+ _cabinGaugeMovieEnabled = false;
}
void Myst::o_generatorControlRoom_exit(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
diff --git a/engines/mohawk/myst_stacks/myst.h b/engines/mohawk/myst_stacks/myst.h
index a83609f640..6e2f7cc1c8 100644
--- a/engines/mohawk/myst_stacks/myst.h
+++ b/engines/mohawk/myst_stacks/myst.h
@@ -40,21 +40,20 @@ public:
Myst(MohawkEngine_Myst *vm);
~Myst();
- virtual void disablePersistentScripts();
- virtual void runPersistentScripts();
+ virtual void disablePersistentScripts() override;
+ virtual void runPersistentScripts() override;
protected:
void setupOpcodes();
- uint16 getVar(uint16 var);
- void toggleVar(uint16 var);
- bool setVarValue(uint16 var, uint16 value);
+ uint16 getVar(uint16 var) override;
+ void toggleVar(uint16 var) override;
+ bool setVarValue(uint16 var, uint16 value) override;
- virtual uint16 getMap() { return 9934; }
+ virtual uint16 getMap() override { return 9934; }
void towerRotationMap_run();
virtual void libraryBookcaseTransform_run();
void generatorControlRoom_run();
- void opcode_212_run();
void libraryCombinationBook_run();
void clockWheel_run();
void matchBurn_run();
@@ -192,11 +191,11 @@ protected:
bool _generatorControlRoomRunning;
uint16 _generatorVoltage; // 58
- MystResourceType10 *_rocketSlider1; // 248
- MystResourceType10 *_rocketSlider2; // 252
- MystResourceType10 *_rocketSlider3; // 256
- MystResourceType10 *_rocketSlider4; // 260
- MystResourceType10 *_rocketSlider5; // 264
+ MystAreaSlider *_rocketSlider1; // 248
+ MystAreaSlider *_rocketSlider2; // 252
+ MystAreaSlider *_rocketSlider3; // 256
+ MystAreaSlider *_rocketSlider4; // 260
+ MystAreaSlider *_rocketSlider5; // 264
uint16 _rocketSliderSound; // 294
uint16 _rocketLeverPosition; // 296
VideoHandle _rocketLinkBook;
@@ -214,7 +213,7 @@ protected:
uint32 _gullsNextTime; // 216
bool _libraryBookcaseMoving;
- MystResourceType6 *_libraryBookcaseMovie; // 104
+ MystAreaVideo *_libraryBookcaseMovie; // 104
uint16 _libraryBookcaseSoundId; // 284
bool _libraryBookcaseChanged; // 288
uint16 _libraryBookSound1; // 298
@@ -223,13 +222,13 @@ protected:
uint16 _courtyardBoxSound; // 302
bool _imagerValidationRunning;
- MystResourceType8 *_imagerRedButton; // 304
+ MystAreaImageSwitch *_imagerRedButton; // 304
uint16 _imagerSound[4]; // 308 to 314
uint16 _imagerValidationCard; // 316
uint16 _imagerValidationStep; // 318
bool _imagerRunning;
- MystResourceType6 *_imagerMovie; // 64
+ MystAreaVideo *_imagerMovie; // 64
uint16 _fireplaceLines[6]; // 74 to 84
@@ -248,8 +247,8 @@ protected:
bool _towerRotationBlinkLabel;
uint16 _towerRotationBlinkLabelCount;
uint16 _towerRotationMapInitialized; // 292
- MystResourceType11 *_towerRotationMapTower; // 108
- MystResourceType8 *_towerRotationMapLabel; // 112
+ MystAreaImageSwitch *_towerRotationMapTower; // 108
+ MystAreaImageSwitch *_towerRotationMapLabel; // 112
uint16 _towerRotationSpeed; // 124
bool _towerRotationMapClicked; // 132
bool _towerRotationOverSpot; // 136
@@ -257,10 +256,13 @@ protected:
bool _matchBurning;
uint16 _matchGoOutCnt;
uint16 _cabinDoorOpened; // 56
+ uint16 _cabinHandleDown; // 344
uint16 _cabinMatchState; // 60
uint32 _matchGoOutTime; // 144
VideoHandle _cabinFireMovie; // 240
+
+ bool _cabinGaugeMovieEnabled;
VideoHandle _cabinGaugeMovie; // 244
bool _boilerPressureIncreasing;
@@ -269,8 +271,8 @@ protected:
bool _basementPressureDecreasing;
bool _treeStopped; // 236
- MystResourceType8 *_tree; // 220
- MystResourceType5 *_treeAlcove; // 224
+ MystAreaImageSwitch *_tree; // 220
+ MystArea *_treeAlcove; // 224
uint16 _treeMinPosition; // 228
uint16 _treeMinAccessiblePosition; // 230
uint16 _treeMaxAccessiblePosition; // 232
@@ -280,21 +282,21 @@ protected:
bool _observatoryDayChanging;
bool _observatoryYearChanging;
bool _observatoryTimeChanging;
- MystResourceType8 *_observatoryVisualizer; // 184
- MystResourceType8 *_observatoryGoButton; // 188
- MystResourceType10 *_observatoryDaySlider; // 192
- MystResourceType10 *_observatoryMonthSlider; // 196
- MystResourceType10 *_observatoryYearSlider; // 200
- MystResourceType10 *_observatoryTimeSlider; // 204
+ MystAreaImageSwitch *_observatoryVisualizer; // 184
+ MystAreaImageSwitch *_observatoryGoButton; // 188
+ MystAreaSlider *_observatoryDaySlider; // 192
+ MystAreaSlider *_observatoryMonthSlider; // 196
+ MystAreaSlider *_observatoryYearSlider; // 200
+ MystAreaSlider *_observatoryTimeSlider; // 204
uint32 _observatoryLastTime; // 208
bool _observatoryNotInitialized; // 212
int16 _observatoryIncrement; // 346
- MystResourceType10 *_observatoryCurrentSlider; // 348
+ MystAreaSlider *_observatoryCurrentSlider; // 348
bool _greenBookRunning;
void generatorRedrawRocket();
- void generatorButtonValue(MystResource *button, uint16 &offset, uint16 &value);
+ void generatorButtonValue(MystArea *button, uint16 &offset, uint16 &value);
void rocketSliderMove();
uint16 rocketSliderGetSound(uint16 pos);
diff --git a/engines/mohawk/myst_stacks/preview.cpp b/engines/mohawk/myst_stacks/preview.cpp
index 4cae4aaca7..42458758f5 100644
--- a/engines/mohawk/myst_stacks/preview.cpp
+++ b/engines/mohawk/myst_stacks/preview.cpp
@@ -239,13 +239,13 @@ void Preview::o_library_init(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
debugC(kDebugScript, "Opcode %d: Library init", op);
// Used for Card 3002 (Myst Island Overview)
- _library = static_cast<MystResourceType8 *>(_invokingResource);
+ _library = getInvokingResource<MystAreaImageSwitch>();
}
void Preview::o_libraryBookcaseTransformDemo_init(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
if (_libraryBookcaseChanged) {
- MystResourceType7 *resource = static_cast<MystResourceType7 *>(_invokingResource);
- _libraryBookcaseMovie = static_cast<MystResourceType6 *>(resource->getSubResource(getVar(303)));
+ MystAreaActionSwitch *resource = getInvokingResource<MystAreaActionSwitch>();
+ _libraryBookcaseMovie = static_cast<MystAreaVideo *>(resource->getSubResource(getVar(303)));
_libraryBookcaseSoundId = argv[0];
_libraryBookcaseMoving = true;
}
diff --git a/engines/mohawk/myst_stacks/preview.h b/engines/mohawk/myst_stacks/preview.h
index 0959e935f5..9d833b35e2 100644
--- a/engines/mohawk/myst_stacks/preview.h
+++ b/engines/mohawk/myst_stacks/preview.h
@@ -40,8 +40,8 @@ public:
Preview(MohawkEngine_Myst *vm);
~Preview();
- void disablePersistentScripts();
- void runPersistentScripts();
+ void disablePersistentScripts() override;
+ void runPersistentScripts() override;
private:
void setupOpcodes();
@@ -56,7 +56,7 @@ private:
DECLARE_OPCODE(o_library_init);
uint16 _libraryState; // 4
- MystResourceType8 *_library; // 32
+ MystAreaImageSwitch *_library; // 32
bool _speechRunning;
uint _speechStep;
@@ -67,7 +67,7 @@ private:
void speech_run();
void speechUpdateCue();
- void libraryBookcaseTransform_run();
+ void libraryBookcaseTransform_run() override;
};
} // End of namespace MystStacks
diff --git a/engines/mohawk/myst_stacks/selenitic.cpp b/engines/mohawk/myst_stacks/selenitic.cpp
index 8b95c7fa53..5402e5a581 100644
--- a/engines/mohawk/myst_stacks/selenitic.cpp
+++ b/engines/mohawk/myst_stacks/selenitic.cpp
@@ -39,9 +39,12 @@ namespace MystStacks {
Selenitic::Selenitic(MohawkEngine_Myst *vm) :
MystScriptParser(vm), _state(vm->_gameState->_selenitic) {
setupOpcodes();
- _invokingResource = NULL;
_mazeRunnerPosition = 288;
_mazeRunnerDirection = 8;
+ _mazeRunnerDoorOpened = false;
+
+ _soundReceiverDirection = 0;
+ _soundReceiverStartTime = 0;
}
Selenitic::~Selenitic() {
@@ -669,14 +672,20 @@ void Selenitic::soundReceiverUpdate() {
}
void Selenitic::soundReceiverDrawView() {
+ soundReceiverSetSubimageRect();
+ soundReceiverDrawAngle();
+}
+
+void Selenitic::soundReceiverSetSubimageRect() const {
uint32 left = ((*_soundReceiverPosition) * 1800) / 3600;
- _soundReceiverViewer->_subImages->rect.left = left;
- _soundReceiverViewer->_subImages->rect.right = left + 136;
+ Common::Rect rect = _soundReceiverViewer->getSubImage(0).rect;
- _soundReceiverViewer->drawConditionalDataToScreen(0);
+ rect.left = left;
+ rect.right = left + 136;
- soundReceiverDrawAngle();
+ _soundReceiverViewer->setSubImageRect(0, rect);
+ _soundReceiverViewer->drawConditionalDataToScreen(0);
}
void Selenitic::soundReceiverDrawAngle() {
@@ -770,7 +779,7 @@ uint16 Selenitic::soundLockCurrentSound(uint16 position, bool pixels) {
return 0;
}
-MystResourceType10 *Selenitic::soundLockSliderFromVar(uint16 var) {
+MystAreaSlider *Selenitic::soundLockSliderFromVar(uint16 var) {
switch (var) {
case 20:
return _soundLockSlider1;
@@ -784,13 +793,13 @@ MystResourceType10 *Selenitic::soundLockSliderFromVar(uint16 var) {
return _soundLockSlider5;
}
- return 0;
+ return nullptr;
}
void Selenitic::o_soundLockMove(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
debugC(kDebugScript, "Opcode %d: Sound lock move", op);
- MystResourceType10 *slider = soundLockSliderFromVar(var);
+ MystAreaSlider *slider = soundLockSliderFromVar(var);
uint16 soundId = soundLockCurrentSound(slider->_pos.y, true);
if (_soundLockSoundId != soundId) {
@@ -802,7 +811,7 @@ void Selenitic::o_soundLockMove(uint16 op, uint16 var, uint16 argc, uint16 *argv
void Selenitic::o_soundLockStartMove(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
debugC(kDebugScript, "Opcode %d: Sound lock start move", op);
- MystResourceType10 *slider = soundLockSliderFromVar(var);
+ MystAreaSlider *slider = soundLockSliderFromVar(var);
_vm->_cursor->setCursor(700);
_vm->_sound->pauseBackgroundMyst();
@@ -814,7 +823,7 @@ void Selenitic::o_soundLockStartMove(uint16 op, uint16 var, uint16 argc, uint16
void Selenitic::o_soundLockEndMove(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
debugC(kDebugScript, "Opcode %d: Sound lock end move", op);
- MystResourceType10 *slider = soundLockSliderFromVar(var);
+ MystAreaSlider *slider = soundLockSliderFromVar(var);
uint16 *value = &_state.soundLockSliderPositions[0];
switch (var) {
@@ -858,7 +867,7 @@ void Selenitic::o_soundLockEndMove(uint16 op, uint16 var, uint16 argc, uint16 *a
_vm->_sound->resumeBackgroundMyst();
}
-void Selenitic::soundLockCheckSolution(MystResourceType10 *slider, uint16 value, uint16 solution, bool &solved) {
+void Selenitic::soundLockCheckSolution(MystAreaSlider *slider, uint16 value, uint16 solution, bool &solved) {
slider->drawConditionalDataToScreen(2);
_vm->_sound->replaceSoundMyst(soundLockCurrentSound(value / 12, false));
_vm->_system->delayMillis(1500);
@@ -926,15 +935,15 @@ void Selenitic::o_soundReceiverEndMove(uint16 op, uint16 var, uint16 argc, uint1
}
void Selenitic::o_mazeRunnerCompass_init(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
- _mazeRunnerCompass = static_cast<MystResourceType8 *>(_invokingResource);
+ _mazeRunnerCompass = getInvokingResource<MystAreaImageSwitch>();
}
void Selenitic::o_mazeRunnerWindow_init(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
- _mazeRunnerWindow = static_cast<MystResourceType8 *>(_invokingResource);
+ _mazeRunnerWindow = getInvokingResource<MystAreaImageSwitch>();
}
void Selenitic::o_mazeRunnerLight_init(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
- _mazeRunnerLight = static_cast<MystResourceType8 *>(_invokingResource);
+ _mazeRunnerLight = getInvokingResource<MystAreaImageSwitch>();
}
void Selenitic::soundReceiver_run() {
@@ -942,10 +951,13 @@ void Selenitic::soundReceiver_run() {
if (_soundReceiverDirection) {
uint32 currentTime = _vm->_system->getMillis();
- if (_soundReceiverSpeed == 50 && currentTime > _soundReceiverStartTime + 500)
- soundReceiverIncreaseSpeed();
- else if (currentTime > _soundReceiverStartTime + 1000)
- soundReceiverIncreaseSpeed();
+ if (_soundReceiverSpeed == 50 && currentTime > _soundReceiverStartTime + 500) {
+ soundReceiverIncreaseSpeed();
+ _soundReceiverStartTime = currentTime;
+ } else if (currentTime > _soundReceiverStartTime + 1000) {
+ soundReceiverIncreaseSpeed();
+ _soundReceiverStartTime = currentTime;
+ }
if (currentTime > _soundReceiverStartTime + 100)
soundReceiverUpdate();
@@ -1056,24 +1068,26 @@ void Selenitic::o_soundReceiver_init(uint16 op, uint16 var, uint16 argc, uint16
// Used for Card 1245 (Sound Receiver)
_soundReceiverRunning = true;
- _soundReceiverRightButton = static_cast<MystResourceType8 *>(_vm->_resources[0]);
- _soundReceiverLeftButton = static_cast<MystResourceType8 *>(_vm->_resources[1]);
- _soundReceiverSigmaButton = static_cast<MystResourceType8 *>(_vm->_resources[2]);
- _soundReceiverSources[4] = static_cast<MystResourceType8 *>(_vm->_resources[3]);
- _soundReceiverSources[3] = static_cast<MystResourceType8 *>(_vm->_resources[4]);
- _soundReceiverSources[2] = static_cast<MystResourceType8 *>(_vm->_resources[5]);
- _soundReceiverSources[1] = static_cast<MystResourceType8 *>(_vm->_resources[6]);
- _soundReceiverSources[0] = static_cast<MystResourceType8 *>(_vm->_resources[7]);
- _soundReceiverViewer = static_cast<MystResourceType8 *>(_vm->_resources[8]);
- _soundReceiverAngle1 = static_cast<MystResourceType8 *>(_vm->_resources[10]);
- _soundReceiverAngle2 = static_cast<MystResourceType8 *>(_vm->_resources[11]);
- _soundReceiverAngle3 = static_cast<MystResourceType8 *>(_vm->_resources[12]);
- _soundReceiverAngle4 = static_cast<MystResourceType8 *>(_vm->_resources[13]);
+ _soundReceiverRightButton = _vm->getViewResource<MystAreaImageSwitch>(0);
+ _soundReceiverLeftButton = _vm->getViewResource<MystAreaImageSwitch>(1);
+ _soundReceiverSigmaButton = _vm->getViewResource<MystAreaImageSwitch>(2);
+ _soundReceiverSources[4] = _vm->getViewResource<MystAreaImageSwitch>(3);
+ _soundReceiverSources[3] = _vm->getViewResource<MystAreaImageSwitch>(4);
+ _soundReceiverSources[2] = _vm->getViewResource<MystAreaImageSwitch>(5);
+ _soundReceiverSources[1] = _vm->getViewResource<MystAreaImageSwitch>(6);
+ _soundReceiverSources[0] = _vm->getViewResource<MystAreaImageSwitch>(7);
+ _soundReceiverViewer = _vm->getViewResource<MystAreaImageSwitch>(8);
+ _soundReceiverAngle1 = _vm->getViewResource<MystAreaImageSwitch>(10);
+ _soundReceiverAngle2 = _vm->getViewResource<MystAreaImageSwitch>(11);
+ _soundReceiverAngle3 = _vm->getViewResource<MystAreaImageSwitch>(12);
+ _soundReceiverAngle4 = _vm->getViewResource<MystAreaImageSwitch>(13);
uint16 currentSource = _state.soundReceiverCurrentSource;
_soundReceiverPosition = &_state.soundReceiverPositions[currentSource];
_soundReceiverCurrentSource = _soundReceiverSources[currentSource];
+ soundReceiverSetSubimageRect();
+
_soundReceiverSigmaPressed = false;
}
@@ -1081,31 +1095,31 @@ void Selenitic::o_soundLock_init(uint16 op, uint16 var, uint16 argc, uint16 *arg
debugC(kDebugScript, "Opcode %d: Sound lock init", op);
for (uint i = 0; i < _vm->_resources.size(); i++) {
- if (_vm->_resources[i]->type == kMystSlider) {
- switch (_vm->_resources[i]->getType8Var()) {
+ if (_vm->_resources[i]->type == kMystAreaSlider) {
+ switch (_vm->_resources[i]->getImageSwitchVar()) {
case 20:
- _soundLockSlider1 = static_cast<MystResourceType10 *>(_vm->_resources[i]);
+ _soundLockSlider1 = _vm->getViewResource<MystAreaSlider>(i);
_soundLockSlider1->setStep(_state.soundLockSliderPositions[0]);
break;
case 21:
- _soundLockSlider2 = static_cast<MystResourceType10 *>(_vm->_resources[i]);
+ _soundLockSlider2 = _vm->getViewResource<MystAreaSlider>(i);
_soundLockSlider2->setStep(_state.soundLockSliderPositions[1]);
break;
case 22:
- _soundLockSlider3 = static_cast<MystResourceType10 *>(_vm->_resources[i]);
+ _soundLockSlider3 = _vm->getViewResource<MystAreaSlider>(i);
_soundLockSlider3->setStep(_state.soundLockSliderPositions[2]);
break;
case 23:
- _soundLockSlider4 = static_cast<MystResourceType10 *>(_vm->_resources[i]);
+ _soundLockSlider4 = _vm->getViewResource<MystAreaSlider>(i);
_soundLockSlider4->setStep(_state.soundLockSliderPositions[3]);
break;
case 24:
- _soundLockSlider5 = static_cast<MystResourceType10 *>(_vm->_resources[i]);
+ _soundLockSlider5 = _vm->getViewResource<MystAreaSlider>(i);
_soundLockSlider5->setStep(_state.soundLockSliderPositions[4]);
break;
}
- } else if (_vm->_resources[i]->type == kMystConditionalImage && _vm->_resources[i]->getType8Var() == 28) {
- _soundLockButton = static_cast<MystResourceType8 *>(_vm->_resources[i]);
+ } else if (_vm->_resources[i]->type == kMystAreaImageSwitch && _vm->_resources[i]->getImageSwitchVar() == 28) {
+ _soundLockButton = _vm->getViewResource<MystAreaImageSwitch>(i);
}
}
@@ -1113,11 +1127,11 @@ void Selenitic::o_soundLock_init(uint16 op, uint16 var, uint16 argc, uint16 *arg
}
void Selenitic::o_mazeRunnerRight_init(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
- _mazeRunnerRightButton = static_cast<MystResourceType8 *>(_invokingResource);
+ _mazeRunnerRightButton = getInvokingResource<MystAreaImageSwitch>();
}
void Selenitic::o_mazeRunnerLeft_init(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
- _mazeRunnerLeftButton = static_cast<MystResourceType8 *>(_invokingResource);
+ _mazeRunnerLeftButton = getInvokingResource<MystAreaImageSwitch>();
}
const uint16 Selenitic::_mazeRunnerMap[300][4] = {
diff --git a/engines/mohawk/myst_stacks/selenitic.h b/engines/mohawk/myst_stacks/selenitic.h
index c669d01012..fc9649755d 100644
--- a/engines/mohawk/myst_stacks/selenitic.h
+++ b/engines/mohawk/myst_stacks/selenitic.h
@@ -29,7 +29,7 @@
namespace Mohawk {
-class MystResourceType8;
+class MystAreaImageSwitch;
struct MystScriptEntry;
namespace MystStacks {
@@ -41,16 +41,16 @@ public:
Selenitic(MohawkEngine_Myst *vm);
~Selenitic();
- void disablePersistentScripts();
- void runPersistentScripts();
+ void disablePersistentScripts() override;
+ void runPersistentScripts() override;
private:
void setupOpcodes();
- uint16 getVar(uint16 var);
- void toggleVar(uint16 var);
- bool setVarValue(uint16 var, uint16 value);
+ uint16 getVar(uint16 var) override;
+ void toggleVar(uint16 var) override;
+ bool setVarValue(uint16 var, uint16 value) override;
- virtual uint16 getMap() { return 9930; }
+ virtual uint16 getMap() override { return 9930; }
DECLARE_OPCODE(o_mazeRunnerMove);
DECLARE_OPCODE(o_mazeRunnerSoundRepeat);
@@ -80,43 +80,44 @@ private:
bool _soundReceiverRunning;
bool _soundReceiverSigmaPressed; // 6
- MystResourceType8 *_soundReceiverSources[5]; // 92 -> 108
- MystResourceType8 *_soundReceiverCurrentSource; // 112
+ MystAreaImageSwitch *_soundReceiverSources[5]; // 92 -> 108
+ MystAreaImageSwitch *_soundReceiverCurrentSource; // 112
uint16 *_soundReceiverPosition; // 116
uint16 _soundReceiverDirection; // 120
uint16 _soundReceiverSpeed; // 122
uint32 _soundReceiverStartTime; //124
- MystResourceType8 *_soundReceiverViewer; // 128
- MystResourceType8 *_soundReceiverRightButton; // 132
- MystResourceType8 *_soundReceiverLeftButton; // 136
- MystResourceType8 *_soundReceiverAngle1; // 140
- MystResourceType8 *_soundReceiverAngle2; // 144
- MystResourceType8 *_soundReceiverAngle3; // 148
- MystResourceType8 *_soundReceiverAngle4; // 152
- MystResourceType8 *_soundReceiverSigmaButton; // 156
+ MystAreaImageSwitch *_soundReceiverViewer; // 128
+ MystAreaImageSwitch *_soundReceiverRightButton; // 132
+ MystAreaImageSwitch *_soundReceiverLeftButton; // 136
+ MystAreaImageSwitch *_soundReceiverAngle1; // 140
+ MystAreaImageSwitch *_soundReceiverAngle2; // 144
+ MystAreaImageSwitch *_soundReceiverAngle3; // 148
+ MystAreaImageSwitch *_soundReceiverAngle4; // 152
+ MystAreaImageSwitch *_soundReceiverSigmaButton; // 156
static const uint16 _mazeRunnerMap[300][4];
static const uint8 _mazeRunnerVideos[300][4];
uint16 _mazeRunnerPosition; // 56
uint16 _mazeRunnerDirection; // 58
- MystResourceType8 *_mazeRunnerWindow; // 68
- MystResourceType8 *_mazeRunnerCompass; // 72
- MystResourceType8 *_mazeRunnerLight; // 76
- MystResourceType8 *_mazeRunnerRightButton; // 80
- MystResourceType8 *_mazeRunnerLeftButton; // 84
+ MystAreaImageSwitch *_mazeRunnerWindow; // 68
+ MystAreaImageSwitch *_mazeRunnerCompass; // 72
+ MystAreaImageSwitch *_mazeRunnerLight; // 76
+ MystAreaImageSwitch *_mazeRunnerRightButton; // 80
+ MystAreaImageSwitch *_mazeRunnerLeftButton; // 84
bool _mazeRunnerDoorOpened; // 160
uint16 _soundLockSoundId;
- MystResourceType10 *_soundLockSlider1; // 164
- MystResourceType10 *_soundLockSlider2; // 168
- MystResourceType10 *_soundLockSlider3; // 172
- MystResourceType10 *_soundLockSlider4; // 176
- MystResourceType10 *_soundLockSlider5; // 180
- MystResourceType8 *_soundLockButton; // 184
+ MystAreaSlider *_soundLockSlider1; // 164
+ MystAreaSlider *_soundLockSlider2; // 168
+ MystAreaSlider *_soundLockSlider3; // 172
+ MystAreaSlider *_soundLockSlider4; // 176
+ MystAreaSlider *_soundLockSlider5; // 180
+ MystAreaImageSwitch *_soundLockButton; // 184
void soundReceiverLeftRight(uint direction);
void soundReceiverUpdate();
+ void soundReceiverSetSubimageRect() const;
void soundReceiverDrawView();
void soundReceiverDrawAngle();
void soundReceiverIncreaseSpeed();
@@ -125,8 +126,8 @@ private:
void soundReceiverSolution(uint16 source, uint16 &solution, bool &enabled);
uint16 soundLockCurrentSound(uint16 position, bool pixels);
- MystResourceType10 *soundLockSliderFromVar(uint16 var);
- void soundLockCheckSolution(MystResourceType10 *slider, uint16 value, uint16 solution, bool &solved);
+ MystAreaSlider *soundLockSliderFromVar(uint16 var);
+ void soundLockCheckSolution(MystAreaSlider *slider, uint16 value, uint16 solution, bool &solved);
bool mazeRunnerForwardAllowed(uint16 position);
void mazeRunnerUpdateCompass();
diff --git a/engines/mohawk/myst_stacks/slides.h b/engines/mohawk/myst_stacks/slides.h
index fb7868a03c..a0c9ae5821 100644
--- a/engines/mohawk/myst_stacks/slides.h
+++ b/engines/mohawk/myst_stacks/slides.h
@@ -40,8 +40,8 @@ public:
Slides(MohawkEngine_Myst *vm);
~Slides();
- void disablePersistentScripts();
- void runPersistentScripts();
+ void disablePersistentScripts() override;
+ void runPersistentScripts() override;
private:
void setupOpcodes();
diff --git a/engines/mohawk/myst_stacks/stoneship.cpp b/engines/mohawk/myst_stacks/stoneship.cpp
index 1113ceeac9..293c0f96f4 100644
--- a/engines/mohawk/myst_stacks/stoneship.cpp
+++ b/engines/mohawk/myst_stacks/stoneship.cpp
@@ -50,6 +50,8 @@ Stoneship::Stoneship(MohawkEngine_Myst *vm) :
_chestDrawersOpen = 0;
_chestAchenarBottomDrawerClosed = 1;
+ _brotherDoorOpen = 0;
+
// Drop key
if (_state.trapdoorKeyState == 1)
_state.trapdoorKeyState = 2;
@@ -402,9 +404,9 @@ void Stoneship::o_pumpTurnOff(uint16 op, uint16 var, uint16 argc, uint16 *argv)
}
for (uint i = 0; i < _vm->_resources.size(); i++) {
- MystResource *resource = _vm->_resources[i];
- if (resource->type == kMystConditionalImage && resource->getType8Var() == buttonVar) {
- static_cast<MystResourceType8 *>(resource)->drawConditionalDataToScreen(0, true);
+ MystArea *resource = _vm->_resources[i];
+ if (resource->type == kMystAreaImageSwitch && resource->getImageSwitchVar() == buttonVar) {
+ static_cast<MystAreaImageSwitch *>(resource)->drawConditionalDataToScreen(0, true);
break;
}
}
@@ -437,9 +439,9 @@ void Stoneship::o_cabinBookMovie(uint16 op, uint16 var, uint16 argc, uint16 *arg
void Stoneship::o_drawerOpenSirius(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
debugC(kDebugScript, "Opcode %d: Open drawer", op);
- MystResourceType8 *drawer = static_cast<MystResourceType8 *>(_vm->_resources[argv[0]]);
+ MystAreaImageSwitch *drawer = _vm->getViewResource<MystAreaImageSwitch>(argv[0]);
- if (drawer->getType8Var() == 35) {
+ if (drawer->getImageSwitchVar() == 35) {
drawer->drawConditionalDataToScreen(getVar(102), 0);
} else {
drawer->drawConditionalDataToScreen(0, 0);
@@ -466,7 +468,7 @@ void Stoneship::o_telescopeStart(uint16 op, uint16 var, uint16 argc, uint16 *arg
void Stoneship::o_telescopeMove(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
debugC(kDebugScript, "Opcode %d: Telescope move", op);
- MystResourceType11 *display = static_cast<MystResourceType11 *>(_invokingResource);
+ MystAreaDrag *display = getInvokingResource<MystAreaDrag>();
const Common::Point &mouse = _vm->_system->getEventManager()->getMousePos();
// Compute telescope position
@@ -489,7 +491,7 @@ void Stoneship::o_telescopeStop(uint16 op, uint16 var, uint16 argc, uint16 *argv
void Stoneship::o_generatorStart(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
debugC(kDebugScript, "Opcode %d: Generator start", op);
- MystResourceType11 *handle = static_cast<MystResourceType11 *>(_invokingResource);
+ MystAreaDrag *handle = getInvokingResource<MystAreaDrag>();
uint16 soundId = handle->getList1(0);
if (soundId)
@@ -504,7 +506,7 @@ void Stoneship::o_generatorStart(uint16 op, uint16 var, uint16 argc, uint16 *arg
_batteryNextTime = _vm->_system->getMillis() + 1000;
// Start handle movie
- MystResourceType6 *movie = static_cast<MystResourceType6 *>(handle->getSubResource(0));
+ MystAreaVideo *movie = static_cast<MystAreaVideo *>(handle->getSubResource(0));
movie->playMovie();
soundId = handle->getList2(0);
@@ -530,8 +532,8 @@ void Stoneship::o_generatorStop(uint16 op, uint16 var, uint16 argc, uint16 *argv
}
// Pause handle movie
- MystResourceType11 *handle = static_cast<MystResourceType11 *>(_invokingResource);
- MystResourceType6 *movie = static_cast<MystResourceType6 *>(handle->getSubResource(0));
+ MystAreaDrag *handle = getInvokingResource<MystAreaDrag>();
+ MystAreaVideo *movie = static_cast<MystAreaVideo *>(handle->getSubResource(0));
movie->pauseMovie(true);
uint16 soundId = handle->getList3(0);
@@ -582,7 +584,7 @@ void Stoneship::batteryDeplete_run() {
void Stoneship::o_drawerOpenAchenar(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
debugC(kDebugScript, "Opcode %d: Open drawer", op);
- MystResourceType8 *drawer = static_cast<MystResourceType8 *>(_vm->_resources[argv[0]]);
+ MystAreaImageSwitch *drawer = _vm->getViewResource<MystAreaImageSwitch>(argv[0]);
drawer->drawConditionalDataToScreen(0, 0);
_vm->_gfx->runTransition(kTransitionTopToBottom, drawer->getRect(), 25, 5);
}
@@ -617,7 +619,7 @@ void Stoneship::o_hologramSelectionStart(uint16 op, uint16 var, uint16 argc, uin
void Stoneship::o_hologramSelectionMove(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
debugC(kDebugScript, "Opcode %d: Hologram move", op);
- MystResourceType11 *handle = static_cast<MystResourceType11 *>(_invokingResource);
+ MystAreaDrag *handle = getInvokingResource<MystAreaDrag>();
const Common::Point &mouse = _vm->_system->getEventManager()->getMousePos();
if (handle->getRect().contains(mouse)) {
@@ -806,7 +808,7 @@ void Stoneship::o_cloudOrbLeave(uint16 op, uint16 var, uint16 argc, uint16 *argv
_cloudOrbMovie->pauseMovie(true);
_vm->_sound->replaceSoundMyst(_cloudOrbStopSound);
- _vm->_gfx->runTransition(kTransitionTopToBottom, _invokingResource->getRect(), 4, 0);
+ _vm->_gfx->runTransition(kTransitionTopToBottom, getInvokingResource<MystArea>()->getRect(), 4, 0);
}
void Stoneship::o_drawerCloseOpened(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
@@ -822,20 +824,20 @@ void Stoneship::drawerClose(uint16 drawer) {
_vm->drawCardBackground();
_vm->drawResourceImages();
- MystResource *res = _vm->_resources[drawer];
+ MystArea *res = _vm->_resources[drawer];
_vm->_gfx->runTransition(kTransitionBottomToTop, res->getRect(), 25, 5);
}
void Stoneship::o_hologramDisplay_init(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
debugC(kDebugScript, "Opcode %d: Hologram display init", op);
- _hologramDisplay = static_cast<MystResourceType6 *>(_invokingResource);
+ _hologramDisplay = getInvokingResource<MystAreaVideo>();
_hologramDisplayPos = 0;
}
void Stoneship::o_hologramSelection_init(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
debugC(kDebugScript, "Opcode %d: Hologram selection init", op);
- _hologramSelection = static_cast<MystResourceType6 *>(_invokingResource);
+ _hologramSelection = getInvokingResource<MystAreaVideo>();
}
void Stoneship::batteryGaugeUpdate() {
@@ -856,7 +858,7 @@ void Stoneship::o_battery_init(uint16 op, uint16 var, uint16 argc, uint16 *argv)
// Used for Card 2160 (Lighthouse Battery Pack Closeup)
debugC(kDebugScript, "Opcode %d: Battery init", op);
- _batteryGauge = static_cast<MystResourceType8 *>(_invokingResource);
+ _batteryGauge = getInvokingResource<MystAreaImageSwitch>();
batteryGaugeUpdate();
}
@@ -1014,7 +1016,7 @@ void Stoneship::o_achenarDrawers_init(uint16 op, uint16 var, uint16 argc, uint16
void Stoneship::o_cloudOrb_init(uint16 op, uint16 var, uint16 argc, uint16 *argv) {
debugC(kDebugScript, "Opcode %d: Cloud orb init", op);
- _cloudOrbMovie = static_cast<MystResourceType6 *>(_invokingResource);
+ _cloudOrbMovie = getInvokingResource<MystAreaVideo>();
_cloudOrbSound = argv[0];
_cloudOrbStopSound = argv[1];
}
diff --git a/engines/mohawk/myst_stacks/stoneship.h b/engines/mohawk/myst_stacks/stoneship.h
index 4e1b42f26b..776641a787 100644
--- a/engines/mohawk/myst_stacks/stoneship.h
+++ b/engines/mohawk/myst_stacks/stoneship.h
@@ -40,16 +40,16 @@ public:
Stoneship(MohawkEngine_Myst *vm);
~Stoneship();
- void disablePersistentScripts();
- void runPersistentScripts();
+ void disablePersistentScripts() override;
+ void runPersistentScripts() override;
private:
void setupOpcodes();
- uint16 getVar(uint16 var);
- void toggleVar(uint16 var);
- bool setVarValue(uint16 var, uint16 value);
+ uint16 getVar(uint16 var) override;
+ void toggleVar(uint16 var) override;
+ bool setVarValue(uint16 var, uint16 value) override;
- virtual uint16 getMap() { return 9933; }
+ virtual uint16 getMap() override { return 9933; }
DECLARE_OPCODE(o_pumpTurnOff);
DECLARE_OPCODE(o_brotherDoorOpen);
@@ -98,7 +98,7 @@ private:
bool _batteryGaugeRunning;
uint16 _batteryLastCharge; // 92
- MystResourceType8 *_batteryGauge; // 96
+ MystAreaImageSwitch *_batteryGauge; // 96
void batteryGaugeUpdate();
void batteryGauge_run();
@@ -113,8 +113,8 @@ private:
void drawerClose(uint16 drawer);
uint16 _hologramTurnedOn; // 80
- MystResourceType6 *_hologramDisplay; // 84
- MystResourceType6 *_hologramSelection; // 88
+ MystAreaVideo *_hologramDisplay; // 84
+ MystAreaVideo *_hologramSelection; // 88
uint16 _hologramDisplayPos;
bool _tunnelRunning;
@@ -135,7 +135,7 @@ private:
void telescope_run();
void telescopeLighthouseDraw();
- MystResourceType6 *_cloudOrbMovie; // 136
+ MystAreaVideo *_cloudOrbMovie; // 136
uint16 _cloudOrbSound; // 140
uint16 _cloudOrbStopSound; // 142
diff --git a/engines/mohawk/myst_state.cpp b/engines/mohawk/myst_state.cpp
index 3e54017df8..06cd69b23c 100644
--- a/engines/mohawk/myst_state.cpp
+++ b/engines/mohawk/myst_state.cpp
@@ -26,11 +26,41 @@
#include "common/debug.h"
#include "common/serializer.h"
+#include "common/system.h"
#include "common/textconsole.h"
#include "common/util.h"
+#include "graphics/thumbnail.h"
+
namespace Mohawk {
+MystSaveMetadata::MystSaveMetadata() {
+ saveDay = 0;
+ saveMonth = 0;
+ saveYear = 0;
+ saveHour = 0;
+ saveMinute = 0;
+ totalPlayTime = 0;
+}
+
+bool MystSaveMetadata::sync(Common::Serializer &s) {
+ static const Common::Serializer::Version kCurrentVersion = 1;
+
+ if (!s.syncVersion(kCurrentVersion)) {
+ return false;
+ }
+
+ s.syncAsByte(saveDay);
+ s.syncAsByte(saveMonth);
+ s.syncAsUint16LE(saveYear);
+ s.syncAsByte(saveHour);
+ s.syncAsByte(saveMinute);
+ s.syncString(saveDescription);
+ s.syncAsUint32LE(totalPlayTime);
+
+ return true;
+}
+
MystGameState::MystGameState(MohawkEngine_Myst *vm, Common::SaveFileManager *saveFileMan) : _vm(vm), _saveFileMan(saveFileMan) {
// Most of the variables are zero at game start.
memset(&_globals, 0, sizeof(_globals));
@@ -77,28 +107,15 @@ MystGameState::~MystGameState() {
}
Common::StringArray MystGameState::generateSaveGameList() {
- return _saveFileMan->listSavefiles("*.mys");
+ return g_system->getSavefileManager()->listSavefiles("*.mys");
}
bool MystGameState::load(const Common::String &filename) {
- Common::InSaveFile *loadFile = _saveFileMan->openForLoading(filename);
- if (!loadFile)
- return false;
-
- debugC(kDebugSaveLoad, "Loading game from '%s'", filename.c_str());
-
- // First, let's make sure we're using a saved game file from this version of Myst
- // By checking length of file...
- int32 size = loadFile->size();
- if (size != 664 && size != 601) {
- warning("Incompatible saved game version");
- delete loadFile;
+ if (!loadState(filename)) {
return false;
}
- Common::Serializer s(loadFile, 0);
- syncGameState(s, size == 664);
- delete loadFile;
+ loadMetadata(filename);
// Set Channelwood elevator state to down, because we start on the lower level
_channelwood.elevatorState = 0;
@@ -119,19 +136,76 @@ bool MystGameState::load(const Common::String &filename) {
return true;
}
-bool MystGameState::save(const Common::String &fname) {
- Common::String filename(fname);
- // Make sure we have the right extension
- if (!filename.hasSuffix(".mys") && !filename.hasSuffix(".MYS"))
- filename += ".mys";
+bool MystGameState::loadState(const Common::String &filename) {
+ Common::InSaveFile *loadFile = _saveFileMan->openForLoading(filename);
+ if (!loadFile) {
+ return false;
+ }
+
+ debugC(kDebugSaveLoad, "Loading game from '%s'", filename.c_str());
+
+ // First, let's make sure we're using a saved game file from this version of Myst
+ // By checking length of file...
+ int32 size = loadFile->size();
+ if (size != 664 && size != 601) {
+ warning("Incompatible saved game version");
+ delete loadFile;
+ return false;
+ }
+
+ Common::Serializer s(loadFile, nullptr);
+ syncGameState(s, size == 664);
+ delete loadFile;
+
+ return true;
+}
+
+void MystGameState::loadMetadata(const Common::String &filename) {
+ // Open the metadata file
+ Common::InSaveFile *metadataFile = openMetadataFile(filename);
+ if (!metadataFile) {
+ return;
+ }
+
+ debugC(kDebugSaveLoad, "Loading metadata from '%s'", filename.c_str());
+
+ Common::Serializer m(metadataFile, nullptr);
+
+ // Read the metadata file
+ if (_metadata.sync(m)) {
+ _vm->setTotalPlayTime(_metadata.totalPlayTime);
+ }
+ delete metadataFile;
+}
+
+bool MystGameState::save(const Common::String &filename) {
+ // Make sure the description does not have an extension
+ Common::String desc = filename;
+ if (filename.hasSuffix(".mys") || filename.hasSuffix(".MYS")) {
+ desc = removeExtension(filename);
+ }
+
+ if (!saveState(desc)) {
+ return false;
+ }
+
+ updateMetadateForSaving(desc);
+
+ return saveMetadata(desc);
+}
+
+bool MystGameState::saveState(const Common::String &desc) {
+ // Make sure we have the right extension
+ Common::String filename = desc + ".mys";
Common::OutSaveFile *saveFile = _saveFileMan->openForSaving(filename);
- if (!saveFile)
+ if (!saveFile) {
return false;
+ }
debugC(kDebugSaveLoad, "Saving game to '%s'", filename.c_str());
- Common::Serializer s(0, saveFile);
+ Common::Serializer s(nullptr, saveFile);
syncGameState(s, _vm->getFeatures() & GF_ME);
saveFile->finalize();
delete saveFile;
@@ -139,6 +213,88 @@ bool MystGameState::save(const Common::String &fname) {
return true;
}
+void MystGameState::updateMetadateForSaving(const Common::String &desc) {
+ // Update save creation info
+ TimeDate t;
+ g_system->getTimeAndDate(t);
+ _metadata.saveYear = t.tm_year + 1900;
+ _metadata.saveMonth = t.tm_mon + 1;
+ _metadata.saveDay = t.tm_mday;
+ _metadata.saveHour = t.tm_hour;
+ _metadata.saveMinute = t.tm_min;
+ _metadata.saveDescription = desc;
+ _metadata.totalPlayTime = _vm->getTotalPlayTime();
+}
+
+bool MystGameState::saveMetadata(const Common::String &desc) {
+ // Write the metadata to a separate file so that the save files
+ // are still compatible with the original engine
+ Common::String metadataFilename = desc + ".mym";
+ Common::OutSaveFile *metadataFile = _saveFileMan->openForSaving(metadataFilename);
+ if (!metadataFile) {
+ return false;
+ }
+
+ // Save the metadata
+ Common::Serializer m(nullptr, metadataFile);
+ _metadata.sync(m);
+
+ // Append a thumbnail
+ Graphics::saveThumbnail(*metadataFile);
+
+ metadataFile->finalize();
+ delete metadataFile;
+
+ return true;
+}
+
+SaveStateDescriptor MystGameState::querySaveMetaInfos(const Common::String filename) {
+ SaveStateDescriptor desc;
+ desc.setDescription(filename);
+
+ // Open the metadata file
+ Common::InSaveFile *metadataFile = openMetadataFile(filename);
+ if (!metadataFile) {
+ return desc;
+ }
+
+ Common::Serializer m(metadataFile, nullptr);
+
+ // Read the metadata file
+ Mohawk::MystSaveMetadata metadata;
+ if (!metadata.sync(m)) {
+ delete metadataFile;
+ return desc;
+ }
+
+ // Set the save description
+ desc.setDescription(metadata.saveDescription);
+ desc.setSaveDate(metadata.saveYear, metadata.saveMonth, metadata.saveDay);
+ desc.setSaveTime(metadata.saveHour, metadata.saveMinute);
+ desc.setPlayTime(metadata.totalPlayTime);
+ desc.setThumbnail(Graphics::loadThumbnail(*metadataFile));
+
+ delete metadataFile;
+
+ return desc;
+}
+
+Common::InSaveFile *MystGameState::openMetadataFile(const Common::String &filename) {
+ // Remove the extension
+ Common::String baseName = removeExtension(filename);
+
+ // Open the metadata file
+ return g_system->getSavefileManager()->openForLoading(baseName + ".mym");
+}
+
+Common::String MystGameState::removeExtension(const Common::String &filename) {
+ Common::String baseName = filename;
+ for (uint i = 0; i < 4; i++) {
+ baseName.deleteLastChar();
+ }
+ return baseName;
+}
+
void MystGameState::syncGameState(Common::Serializer &s, bool isME) {
// Globals first
s.syncAsUint16LE(_globals.u0);
@@ -317,11 +473,14 @@ void MystGameState::syncGameState(Common::Serializer &s, bool isME) {
void MystGameState::deleteSave(const Common::String &saveName) {
debugC(kDebugSaveLoad, "Deleting save file \'%s\'", saveName.c_str());
- _saveFileMan->removeSavefile(saveName.c_str());
+ Common::String basename = removeExtension(saveName);
+
+ g_system->getSavefileManager()->removeSavefile(saveName);
+ g_system->getSavefileManager()->removeSavefile(basename + ".mym");
}
void MystGameState::addZipDest(uint16 stack, uint16 view) {
- ZipDests *zipDests = 0;
+ ZipDests *zipDests = nullptr;
// The demo has no zip dest storage
if (_vm->getFeatures() & GF_DEMO)
diff --git a/engines/mohawk/myst_state.h b/engines/mohawk/myst_state.h
index b07a0f2469..50359a5b52 100644
--- a/engines/mohawk/myst_state.h
+++ b/engines/mohawk/myst_state.h
@@ -27,6 +27,8 @@
#include "common/file.h"
#include "common/str.h"
+#include "engines/savestate.h"
+
namespace Common {
class Serializer;
}
@@ -35,15 +37,33 @@ namespace Mohawk {
class MohawkEngine_Myst;
+struct MystSaveMetadata {
+ uint8 saveDay;
+ uint8 saveMonth;
+ uint16 saveYear;
+
+ uint8 saveHour;
+ uint8 saveMinute;
+
+ uint32 totalPlayTime;
+
+ Common::String saveDescription;
+
+ MystSaveMetadata();
+ bool sync(Common::Serializer &s);
+};
+
class MystGameState {
public:
MystGameState(MohawkEngine_Myst*, Common::SaveFileManager*);
~MystGameState();
- Common::StringArray generateSaveGameList();
- bool load(const Common::String &);
- bool save(const Common::String &);
- void deleteSave(const Common::String &);
+ static Common::StringArray generateSaveGameList();
+ static SaveStateDescriptor querySaveMetaInfos(const Common::String filename);
+
+ bool load(const Common::String &filename);
+ bool save(const Common::String &filename);
+ static void deleteSave(const Common::String &saveName);
void addZipDest(uint16 stack, uint16 view);
bool isReachableZipDest(uint16 stack, uint16 view);
@@ -268,8 +288,17 @@ public:
uint32 generatorDepletionTime;
} _stoneship;
+ MystSaveMetadata _metadata;
+
private:
void syncGameState(Common::Serializer &s, bool isME);
+ static Common::InSaveFile *openMetadataFile(const Common::String &filename);
+ bool loadState(const Common::String &filename);
+ void loadMetadata(const Common::String &filename);
+ bool saveState(const Common::String &desc);
+ void updateMetadateForSaving(const Common::String &desc);
+ bool saveMetadata(const Common::String &desc);
+ static Common::String removeExtension(const Common::String &filename);
// The values in these regions are lists of VIEW resources
// which correspond to visited zip destinations
diff --git a/engines/mohawk/sound.cpp b/engines/mohawk/sound.cpp
index 198627e012..a2c08d4a92 100644
--- a/engines/mohawk/sound.cpp
+++ b/engines/mohawk/sound.cpp
@@ -21,6 +21,7 @@
*/
#include "common/debug.h"
+#include "common/events.h"
#include "common/system.h"
#include "common/util.h"
#include "common/textconsole.h"
@@ -163,8 +164,26 @@ Audio::SoundHandle *Sound::replaceSoundMyst(uint16 id, byte volume, bool loop) {
void Sound::playSoundBlocking(uint16 id, byte volume) {
Audio::SoundHandle *handle = playSound(id, volume);
- while (_vm->_mixer->isSoundHandleActive(*handle))
+ while (_vm->_mixer->isSoundHandleActive(*handle) && !_vm->shouldQuit()) {
+ Common::Event event;
+ while (_vm->_system->getEventManager()->pollEvent(event)) {
+ switch (event.type) {
+ case Common::EVENT_KEYDOWN:
+ switch (event.kbd.keycode) {
+ case Common::KEYCODE_SPACE:
+ _vm->pauseGame();
+ break;
+ default:
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ // Cut down on CPU usage
_vm->_system->delayMillis(10);
+ }
}
void Sound::playMidi(uint16 id) {
@@ -625,7 +644,7 @@ uint16 Sound::convertMystID(uint16 id) {
return id;
}
-Audio::SoundHandle *Sound::replaceBackgroundMyst(uint16 id, uint16 volume) {
+void Sound::replaceBackgroundMyst(uint16 id, uint16 volume) {
debug(0, "Replacing background sound with %d", id);
// TODO: The original engine does fading
@@ -641,8 +660,11 @@ Audio::SoundHandle *Sound::replaceBackgroundMyst(uint16 id, uint16 volume) {
// Check if sound is already playing
if (_mystBackgroundSound.type == kUsedHandle && _vm->_mixer->isSoundHandleActive(_mystBackgroundSound.handle)
- && _vm->getResourceName(ID_MSND, convertMystID(_mystBackgroundSound.id)).hasPrefix(prefix))
- return &_mystBackgroundSound.handle;
+ && _vm->getResourceName(ID_MSND, convertMystID(_mystBackgroundSound.id)).hasPrefix(prefix)) {
+ // The sound is already playing, just change the volume
+ changeBackgroundVolumeMyst(volume);
+ return;
+ }
// Stop old background sound
stopBackgroundMyst();
@@ -659,10 +681,7 @@ Audio::SoundHandle *Sound::replaceBackgroundMyst(uint16 id, uint16 volume) {
Audio::AudioStream *audStream = Audio::makeLoopingAudioStream(rewindStream, 0);
_vm->_mixer->playStream(Audio::Mixer::kPlainSoundType, &_mystBackgroundSound.handle, audStream, -1, volume >> 8);
- return &_mystBackgroundSound.handle;
}
-
- return NULL;
}
void Sound::stopBackgroundMyst() {
diff --git a/engines/mohawk/sound.h b/engines/mohawk/sound.h
index 75c9492d96..c62e6e9874 100644
--- a/engines/mohawk/sound.h
+++ b/engines/mohawk/sound.h
@@ -136,7 +136,7 @@ public:
// Myst-specific sound functions
Audio::SoundHandle *replaceSoundMyst(uint16 id, byte volume = Audio::Mixer::kMaxChannelVolume, bool loop = false);
- Audio::SoundHandle *replaceBackgroundMyst(uint16 id, uint16 volume = 0xFFFF);
+ void replaceBackgroundMyst(uint16 id, uint16 volume = 0xFFFF);
void pauseBackgroundMyst();
void resumeBackgroundMyst();
void stopBackgroundMyst();
diff --git a/engines/mohawk/video.cpp b/engines/mohawk/video.cpp
index ff4a69cd28..eec543235e 100644
--- a/engines/mohawk/video.cpp
+++ b/engines/mohawk/video.cpp
@@ -146,7 +146,7 @@ VideoHandle::VideoHandle(const VideoHandle &handle) : _ptr(handle._ptr) {
VideoManager::VideoManager(MohawkEngine* vm) : _vm(vm) {
// Set dithering enabled, if required
- _enableDither = _vm->getGameType() == GType_MYST && !(_vm->getFeatures() & GF_ME);
+ _enableDither = (_vm->getGameType() == GType_MYST || _vm->getGameType() == GType_MAKINGOF) && !(_vm->getFeatures() & GF_ME);
}
VideoManager::~VideoManager() {
@@ -184,7 +184,7 @@ void VideoManager::playMovieBlocking(const Common::String &fileName, uint16 x, u
}
ptr->start();
- waitUntilMovieEnds(ptr);
+ waitUntilMovieEnds(VideoHandle(ptr));
}
void VideoManager::playMovieBlockingCentered(const Common::String &fileName, bool clearScreen) {
@@ -200,7 +200,7 @@ void VideoManager::playMovieBlockingCentered(const Common::String &fileName, boo
ptr->center();
ptr->start();
- waitUntilMovieEnds(ptr);
+ waitUntilMovieEnds(VideoHandle(ptr));
}
void VideoManager::waitUntilMovieEnds(VideoHandle videoHandle) {
@@ -278,7 +278,7 @@ VideoHandle VideoManager::playMovie(const Common::String &fileName) {
return VideoHandle();
ptr->start();
- return ptr;
+ return VideoHandle(ptr);
}
VideoHandle VideoManager::playMovie(uint16 id) {
@@ -287,7 +287,7 @@ VideoHandle VideoManager::playMovie(uint16 id) {
return VideoHandle();
ptr->start();
- return ptr;
+ return VideoHandle(ptr);
}
bool VideoManager::updateMovies() {
@@ -317,50 +317,13 @@ bool VideoManager::updateMovies() {
// Check if we need to draw a frame
if (video->needsUpdate()) {
- const Graphics::Surface *frame = video->decodeNextFrame();
- Graphics::Surface *convertedFrame = 0;
-
- if (frame && (*it)->isEnabled()) {
- Graphics::PixelFormat pixelFormat = _vm->_system->getScreenFormat();
-
- if (frame->format != pixelFormat) {
- // We don't support downconverting to 8bpp without having
- // support in the codec. Set _enableDither if shows up.
- if (pixelFormat.bytesPerPixel == 1) {
- warning("Cannot convert high color video frame to 8bpp");
- (*it)->close();
- it = _videos.erase(it);
- continue;
- }
-
- // Convert to the current screen format
- convertedFrame = frame->convertTo(pixelFormat, video->getPalette());
- frame = convertedFrame;
- } else if (pixelFormat.bytesPerPixel == 1 && video->hasDirtyPalette()) {
- // Set the palette when running in 8bpp mode only
- // Don't do this for Myst, which has its own per-stack handling
- if (_vm->getGameType() != GType_MYST)
- _vm->_system->getPaletteManager()->setPalette(video->getPalette(), 0, 256);
- }
-
- // Clip the width/height to make sure we stay on the screen (Myst does this a few times)
- uint16 width = MIN<int32>(video->getWidth(), _vm->_system->getWidth() - (*it)->getX());
- uint16 height = MIN<int32>(video->getHeight(), _vm->_system->getHeight() - (*it)->getY());
- _vm->_system->copyRectToScreen(frame->getPixels(), frame->pitch, (*it)->getX(), (*it)->getY(), width, height);
-
- // We've drawn something to the screen, make sure we update it
+ if (drawNextFrame(*it)) {
updateScreen = true;
-
- // Delete 8bpp conversion surface
- if (convertedFrame) {
- convertedFrame->free();
- delete convertedFrame;
- }
}
}
// Check the video time
- _vm->doVideoTimer(*it, false);
+ _vm->doVideoTimer(VideoHandle(*it), false);
// Remember to increase the iterator
it++;
@@ -370,6 +333,74 @@ bool VideoManager::updateMovies() {
return updateScreen;
}
+bool VideoManager::drawNextFrame(VideoEntryPtr videoEntry) {
+ Video::VideoDecoder *video = videoEntry->_video;
+ const Graphics::Surface *frame = video->decodeNextFrame();
+
+ if (!frame || !videoEntry->isEnabled()) {
+ return false;
+ }
+
+ Graphics::Surface *convertedFrame = 0;
+ Graphics::PixelFormat pixelFormat = _vm->_system->getScreenFormat();
+
+ if (frame->format != pixelFormat) {
+ // We don't support downconverting to 8bpp without having
+ // support in the codec. Set _enableDither if shows up.
+ if (pixelFormat.bytesPerPixel == 1) {
+ warning("Cannot convert high color video frame to 8bpp");
+ return false;
+ }
+
+ // Convert to the current screen format
+ convertedFrame = frame->convertTo(pixelFormat, video->getPalette());
+ frame = convertedFrame;
+ } else if (pixelFormat.bytesPerPixel == 1 && video->hasDirtyPalette()) {
+ // Set the palette when running in 8bpp mode only
+ // Don't do this for Myst, which has its own per-stack handling
+ if (_vm->getGameType() != GType_MYST)
+ _vm->_system->getPaletteManager()->setPalette(video->getPalette(), 0, 256);
+ }
+
+ // Clip the video to make sure it stays on the screen (Myst does this a few times)
+ Common::Rect targetRect = Common::Rect(video->getWidth(), video->getHeight());
+ targetRect.translate(videoEntry->getX(), videoEntry->getY());
+
+ Common::Rect frameRect = Common::Rect(video->getWidth(), video->getHeight());
+
+ if (targetRect.left < 0) {
+ frameRect.left -= targetRect.left;
+ targetRect.left = 0;
+ }
+
+ if (targetRect.top < 0) {
+ frameRect.top -= targetRect.top;
+ targetRect.top = 0;
+ }
+
+ if (targetRect.right > _vm->_system->getWidth()) {
+ frameRect.right -= targetRect.right - _vm->_system->getWidth();
+ targetRect.right = _vm->_system->getWidth();
+ }
+
+ if (targetRect.bottom > _vm->_system->getHeight()) {
+ frameRect.bottom -= targetRect.bottom - _vm->_system->getHeight();
+ targetRect.bottom = _vm->_system->getHeight();
+ }
+
+ _vm->_system->copyRectToScreen(frame->getBasePtr(frameRect.left, frameRect.top), frame->pitch,
+ targetRect.left, targetRect.top, targetRect.width(), targetRect.height());
+
+ // Delete 8bpp conversion surface
+ if (convertedFrame) {
+ convertedFrame->free();
+ delete convertedFrame;
+ }
+
+ // We've drawn something to the screen, make sure we update it
+ return true;
+}
+
void VideoManager::activateMLST(uint16 mlstId, uint16 card) {
Common::SeekableReadStream *mlstStream = _vm->getResource(ID_MLST, card);
uint16 recordCount = mlstStream->readUint16BE();
@@ -430,7 +461,7 @@ VideoHandle VideoManager::playMovieRiven(uint16 id) {
ptr->start();
}
- return ptr;
+ return VideoHandle(ptr);
}
}
@@ -445,7 +476,7 @@ void VideoManager::playMovieBlockingRiven(uint16 id) {
ptr->moveTo(_mlstRecords[i].left, _mlstRecords[i].top);
ptr->setVolume(_mlstRecords[i].volume);
ptr->start();
- waitUntilMovieEnds(ptr);
+ waitUntilMovieEnds(VideoHandle(ptr));
return;
}
}
@@ -522,7 +553,7 @@ VideoHandle VideoManager::findVideoHandleRiven(uint16 id) {
if (_mlstRecords[i].code == id)
for (VideoList::iterator it = _videos.begin(); it != _videos.end(); it++)
if ((*it)->getID() == _mlstRecords[i].movieID)
- return *it;
+ return VideoHandle(*it);
return VideoHandle();
}
@@ -533,7 +564,7 @@ VideoHandle VideoManager::findVideoHandle(uint16 id) {
for (VideoList::iterator it = _videos.begin(); it != _videos.end(); it++)
if ((*it)->getID() == id)
- return *it;
+ return VideoHandle(*it);
return VideoHandle();
}
@@ -544,7 +575,7 @@ VideoHandle VideoManager::findVideoHandle(const Common::String &fileName) {
for (VideoList::iterator it = _videos.begin(); it != _videos.end(); it++)
if ((*it)->getFileName().equalsIgnoreCase(fileName))
- return *it;
+ return VideoHandle(*it);
return VideoHandle();
}
@@ -558,12 +589,10 @@ bool VideoManager::isVideoPlaying() {
}
void VideoManager::drawVideoFrame(VideoHandle handle, const Audio::Timestamp &time) {
- // FIXME: This should be done separately from the "playing"
- // videos eventually.
assert(handle);
handle->seek(time);
- updateMovies();
- handle->close();
+ drawNextFrame(handle._ptr);
+ handle->stop();
}
VideoManager::VideoList::iterator VideoManager::findEntry(VideoEntryPtr ptr) {
diff --git a/engines/mohawk/video.h b/engines/mohawk/video.h
index 106a32f8e2..d0edab9def 100644
--- a/engines/mohawk/video.h
+++ b/engines/mohawk/video.h
@@ -293,7 +293,7 @@ private:
/**
* Constructor for internal VideoManager use
*/
- VideoHandle(VideoEntryPtr ptr);
+ explicit VideoHandle(VideoEntryPtr ptr);
/**
* The video entry this is associated with
@@ -350,6 +350,8 @@ private:
VideoList::iterator findEntry(VideoEntryPtr ptr);
void removeEntry(VideoEntryPtr ptr);
+ bool drawNextFrame(VideoEntryPtr videoEntry);
+
// Dithering control
bool _enableDither;
void checkEnableDither(VideoEntryPtr &entry);
diff --git a/engines/mortevielle/configure.engine b/engines/mortevielle/configure.engine
index a7fb2ccda6..0fe89acc99 100644
--- a/engines/mortevielle/configure.engine
+++ b/engines/mortevielle/configure.engine
@@ -1,3 +1,3 @@
# This file is included from the main "configure" script
# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps]
-add_engine mortevielle "Mortevielle" yes
+add_engine mortevielle "Mortevielle" yes "" "" "highres"
diff --git a/engines/mortevielle/detection.cpp b/engines/mortevielle/detection.cpp
index b6c27e6b12..6791707dd5 100644
--- a/engines/mortevielle/detection.cpp
+++ b/engines/mortevielle/detection.cpp
@@ -55,7 +55,7 @@ public:
MortevielleMetaEngine() : AdvancedMetaEngine(Mortevielle::MortevielleGameDescriptions, sizeof(Mortevielle::MortevielleGameDescription),
MortevielleGame) {
_md5Bytes = 512;
- _singleid = "mortevielle";
+ _singleId = "mortevielle";
// Use kADFlagUseExtraAsHint to distinguish between original and improved versions
// (i.e. use or not of the game data file).
_flags = kADFlagUseExtraAsHint;
diff --git a/engines/neverhood/configure.engine b/engines/neverhood/configure.engine
index 46910e293e..f04e6c69f6 100644
--- a/engines/neverhood/configure.engine
+++ b/engines/neverhood/configure.engine
@@ -1,3 +1,3 @@
# This file is included from the main "configure" script
# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps]
-add_engine neverhood "Neverhood" yes
+add_engine neverhood "Neverhood" yes "" "" "highres"
diff --git a/engines/neverhood/detection.cpp b/engines/neverhood/detection.cpp
index 4d545e136c..cfddc2d6b4 100644
--- a/engines/neverhood/detection.cpp
+++ b/engines/neverhood/detection.cpp
@@ -41,7 +41,7 @@ struct NeverhoodGameDescription {
};
const char *NeverhoodEngine::getGameId() const {
- return _gameDescription->desc.gameid;
+ return _gameDescription->desc.gameId;
}
uint32 NeverhoodEngine::getFeatures() const {
@@ -181,8 +181,8 @@ static const ExtraGuiOption neverhoodExtraGuiOption3 = {
class NeverhoodMetaEngine : public AdvancedMetaEngine {
public:
NeverhoodMetaEngine() : AdvancedMetaEngine(Neverhood::gameDescriptions, sizeof(Neverhood::NeverhoodGameDescription), neverhoodGames) {
- _singleid = "neverhood";
- _guioptions = GUIO2(GUIO_NOSUBTITLES, GUIO_NOMIDI);
+ _singleId = "neverhood";
+ _guiOptions = GUIO2(GUIO_NOSUBTITLES, GUIO_NOMIDI);
}
virtual const char *getName() const {
@@ -245,7 +245,6 @@ SaveStateList NeverhoodMetaEngine::listSaves(const char *target) const {
Common::StringArray filenames;
filenames = saveFileMan->listSavefiles(pattern.c_str());
- Common::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++) {
@@ -262,6 +261,8 @@ SaveStateList NeverhoodMetaEngine::listSaves(const char *target) const {
}
}
+ // Sort saves based on slot number.
+ Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator());
return saveList;
}
diff --git a/engines/parallaction/detection.cpp b/engines/parallaction/detection.cpp
index daeee1fee4..4c52990874 100644
--- a/engines/parallaction/detection.cpp
+++ b/engines/parallaction/detection.cpp
@@ -220,7 +220,7 @@ static const PARALLACTIONGameDescription gameDescriptions[] = {
class ParallactionMetaEngine : public AdvancedMetaEngine {
public:
ParallactionMetaEngine() : AdvancedMetaEngine(Parallaction::gameDescriptions, sizeof(Parallaction::PARALLACTIONGameDescription), parallactionGames) {
- _guioptions = GUIO1(GUIO_NOLAUNCHLOAD);
+ _guiOptions = GUIO1(GUIO_NOLAUNCHLOAD);
}
virtual const char *getName() const {
@@ -273,7 +273,6 @@ SaveStateList ParallactionMetaEngine::listSaves(const char *target) const {
Common::String pattern(ConfMan.getDomain(target)->getVal("gameid") + ".0##");
Common::StringArray 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) {
@@ -290,6 +289,8 @@ SaveStateList ParallactionMetaEngine::listSaves(const char *target) const {
}
}
+ // Sort saves based on slot number.
+ Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator());
return saveList;
}
diff --git a/engines/parallaction/saveload.cpp b/engines/parallaction/saveload.cpp
index 72e9400acd..6d598d9557 100644
--- a/engines/parallaction/saveload.cpp
+++ b/engines/parallaction/saveload.cpp
@@ -194,7 +194,7 @@ int SaveLoad::selectSaveFile(Common::String &selectedName, bool saveMode, const
bool SaveLoad::loadGame() {
Common::String null;
- int _di = selectSaveFile(null, false, "Load file", "Load");
+ int _di = selectSaveFile(null, false, _("Load file"), _("Load"));
if (_di == -1) {
return false;
}
@@ -209,7 +209,7 @@ bool SaveLoad::loadGame() {
bool SaveLoad::saveGame() {
Common::String saveName;
- int slot = selectSaveFile(saveName, true, "Save file", "Save");
+ int slot = selectSaveFile(saveName, true, _("Save file"), _("Save"));
if (slot == -1) {
return false;
}
diff --git a/engines/pegasus/configure.engine b/engines/pegasus/configure.engine
index ed7e295287..650d57a602 100644
--- a/engines/pegasus/configure.engine
+++ b/engines/pegasus/configure.engine
@@ -1,3 +1,3 @@
# This file is included from the main "configure" script
# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps]
-add_engine pegasus "The Journeyman Project: Pegasus Prime" yes "" "" "16bit"
+add_engine pegasus "The Journeyman Project: Pegasus Prime" yes "" "" "16bit highres"
diff --git a/engines/pegasus/detection.cpp b/engines/pegasus/detection.cpp
index 721c382d4f..161a133c8b 100644
--- a/engines/pegasus/detection.cpp
+++ b/engines/pegasus/detection.cpp
@@ -134,7 +134,7 @@ static const PegasusGameDescription gameDescriptions[] = {
class PegasusMetaEngine : public AdvancedMetaEngine {
public:
PegasusMetaEngine() : AdvancedMetaEngine(Pegasus::gameDescriptions, sizeof(Pegasus::PegasusGameDescription), pegasusGames) {
- _singleid = "pegasus";
+ _singleId = "pegasus";
}
virtual const char *getName() const {
diff --git a/engines/prince/configure.engine b/engines/prince/configure.engine
index 50740d9f41..827579b00d 100644
--- a/engines/prince/configure.engine
+++ b/engines/prince/configure.engine
@@ -1,3 +1,3 @@
# This file is included from the main "configure" script
# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps]
-add_engine prince "The Prince and The Coward" no
+add_engine prince "The Prince and The Coward" no "" "" "highres"
diff --git a/engines/prince/detection.cpp b/engines/prince/detection.cpp
index 3fe7993fdb..1c6f63aff3 100644
--- a/engines/prince/detection.cpp
+++ b/engines/prince/detection.cpp
@@ -29,7 +29,7 @@ int PrinceEngine::getGameType() const {
}
const char *PrinceEngine::getGameId() const {
- return _gameDescription->desc.gameid;
+ return _gameDescription->desc.gameId;
}
uint32 PrinceEngine::getFeatures() const {
diff --git a/engines/prince/detection.h b/engines/prince/detection.h
index 7e5bdd6b7b..3076253cf5 100644
--- a/engines/prince/detection.h
+++ b/engines/prince/detection.h
@@ -104,7 +104,7 @@ const static char *directoryGlobs[] = {
class PrinceMetaEngine : public AdvancedMetaEngine {
public:
PrinceMetaEngine() : AdvancedMetaEngine(Prince::gameDescriptions, sizeof(Prince::PrinceGameDescription), princeGames) {
- _singleid = "prince";
+ _singleId = "prince";
_maxScanDepth = 2;
_directoryGlobs = directoryGlobs;
}
diff --git a/engines/prince/graphics.cpp b/engines/prince/graphics.cpp
index 3482d79f69..d5178efc17 100644
--- a/engines/prince/graphics.cpp
+++ b/engines/prince/graphics.cpp
@@ -279,7 +279,7 @@ void GraphicsMan::drawTransparentWithTransDrawNode(Graphics::Surface *screen, Dr
// first and last row at the same time (height = 1) - no anti-alias
continue;
}
- // new color value based on orginal screen surface color and sprite's edge pixel color
+ // new color value based on original screen surface color and sprite's edge pixel color
*dst2 = transTableData[*dst2 * 256 + value];
}
}
diff --git a/engines/queen/detection.cpp b/engines/queen/detection.cpp
index 96075a0f0c..81e0767836 100644
--- a/engines/queen/detection.cpp
+++ b/engines/queen/detection.cpp
@@ -185,6 +185,19 @@ static const QueenGameDescription gameDescriptions[] = {
},
},
+ // DOS Floppy - Russian
+ {
+ {
+ "queen",
+ "Floppy",
+ AD_ENTRY1s("queen.1", "0d1c10d5c3a1bd90bc0b3859a3258093", 22677657),
+ Common::RU_RUS,
+ Common::kPlatformDOS,
+ ADGF_NO_FLAGS,
+ GUIO1(GUIO_NOSPEECH)
+ },
+ },
+
// DOS CD - French
{
{
@@ -388,7 +401,7 @@ static const QueenGameDescription gameDescriptions[] = {
class QueenMetaEngine : public AdvancedMetaEngine {
public:
QueenMetaEngine() : AdvancedMetaEngine(Queen::gameDescriptions, sizeof(Queen::QueenGameDescription), queenGames, optionsList) {
- _singleid = "queen";
+ _singleId = "queen";
}
virtual const char *getName() const {
@@ -430,25 +443,25 @@ const ADGameDescription *QueenMetaEngine::fallbackDetect(const FileMap &allFiles
}
Queen::DetectedGameVersion version;
if (Queen::Resource::detectVersion(&version, &dataFile)) {
- desc.gameid = "queen";
+ desc.gameId = "queen";
desc.language = version.language;
desc.platform = version.platform;
desc.flags = ADGF_NO_FLAGS;
- desc.guioptions = GUIO0();
+ desc.guiOptions = GUIO0();
if (version.features & Queen::GF_DEMO) {
desc.extra = "Demo";
desc.flags = ADGF_DEMO;
- desc.guioptions = GUIO_NOSPEECH;
+ desc.guiOptions = GUIO_NOSPEECH;
} else if (version.features & Queen::GF_INTERVIEW) {
desc.extra = "Interview";
desc.flags = ADGF_DEMO;
- desc.guioptions = GUIO_NOSPEECH;
+ desc.guiOptions = GUIO_NOSPEECH;
} else if (version.features & Queen::GF_FLOPPY) {
desc.extra = "Floppy";
- desc.guioptions = GUIO_NOSPEECH;
+ desc.guiOptions = GUIO_NOSPEECH;
} else if (version.features & Queen::GF_TALKIE) {
desc.extra = "Talkie";
- desc.guioptions = GAMEOPTION_ALT_INTRO;
+ desc.guiOptions = GAMEOPTION_ALT_INTRO;
}
return (const ADGameDescription *)&desc;
}
@@ -464,7 +477,6 @@ SaveStateList QueenMetaEngine::listSaves(const char *target) const {
Common::String pattern("queen.s##");
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) {
@@ -483,6 +495,8 @@ SaveStateList QueenMetaEngine::listSaves(const char *target) const {
}
}
+ // Sort saves based on slot number.
+ Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator());
return saveList;
}
diff --git a/engines/saga/configure.engine b/engines/saga/configure.engine
index 99e2ab367b..adb904a6dd 100644
--- a/engines/saga/configure.engine
+++ b/engines/saga/configure.engine
@@ -1,5 +1,5 @@
# This file is included from the main "configure" script
# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps]
add_engine saga "SAGA" yes "ihnm saga2" "ITE"
-add_engine ihnm "IHNM" yes
-add_engine saga2 "SAGA 2 games" no
+add_engine ihnm "IHNM" yes "" "" "highres"
+add_engine saga2 "SAGA 2 games" no "" "" "highres"
diff --git a/engines/saga/detection.cpp b/engines/saga/detection.cpp
index 13ca63525a..0677e84d67 100644
--- a/engines/saga/detection.cpp
+++ b/engines/saga/detection.cpp
@@ -102,11 +102,11 @@ static const Engines::ObsoleteGameID obsoleteGameIDsTable[] = {
class SagaMetaEngine : public AdvancedMetaEngine {
public:
SagaMetaEngine() : AdvancedMetaEngine(Saga::gameDescriptions, sizeof(Saga::SAGAGameDescription), sagaGames) {
- _singleid = "saga";
+ _singleId = "saga";
}
- virtual GameDescriptor findGame(const char *gameid) const {
- return Engines::findGameID(gameid, _gameids, obsoleteGameIDsTable);
+ virtual GameDescriptor findGame(const char *gameId) const {
+ return Engines::findGameID(gameId, _gameIds, obsoleteGameIDsTable);
}
virtual const char *getName() const {
@@ -183,7 +183,6 @@ SaveStateList SagaMetaEngine::listSaves(const char *target) const {
pattern += ".s##";
filenames = saveFileMan->listSavefiles(pattern);
- sort(filenames.begin(), filenames.end()); // Sort (hopefully ensuring we are sorted numerically..)
SaveStateList saveList;
int slotNum = 0;
@@ -203,6 +202,8 @@ SaveStateList SagaMetaEngine::listSaves(const char *target) const {
}
}
+ // Sort saves based on slot number.
+ Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator());
return saveList;
}
diff --git a/engines/saga/interface.cpp b/engines/saga/interface.cpp
index 44581f26fc..ad940aaf8b 100644
--- a/engines/saga/interface.cpp
+++ b/engines/saga/interface.cpp
@@ -332,6 +332,9 @@ void Interface::saveReminderCallback(void *refCon) {
}
void Interface::updateSaveReminder() {
+ // CHECKME: This is potentially called from a different thread because it is
+ // called from a timer callback. However, it does not seem to take any
+ // precautions to avoid race conditions.
if (_active && _panelMode == kPanelMain) {
_saveReminderState = _saveReminderState % _vm->getDisplayInfo().saveReminderNumSprites + 1;
drawStatusBar();
@@ -1859,8 +1862,10 @@ void Interface::drawStatusBar() {
int stringWidth;
int color;
// The default colors in the Spanish version of IHNM are shifted by one
- // Fixes bug #1848016 - "IHNM: Wrong Subtitles Color (Spanish)"
- int offset = (_vm->getLanguage() == Common::ES_ESP) ? 1 : 0;
+ // Fixes bug #1848016 - "IHNM: Wrong Subtitles Color (Spanish)". This
+ // also applies to the German and French versions (bug #7064 - "IHNM:
+ // text mistake in german version").
+ int offset = (_vm->getLanguage() == Common::ES_ESP || _vm->getLanguage() == Common::DE_DEU || _vm->getLanguage() == Common::FR_FRA) ? 1 : 0;
// Disable the status text in IHNM when the chapter is 8
if (_vm->getGameId() == GID_IHNM && _vm->_scene->currentChapterNumber() == 8)
diff --git a/engines/saga/music.cpp b/engines/saga/music.cpp
index 663f5991d0..cd48ebaa4d 100644
--- a/engines/saga/music.cpp
+++ b/engines/saga/music.cpp
@@ -224,6 +224,9 @@ void Music::musicVolumeGaugeCallback(void *refCon) {
}
void Music::musicVolumeGauge() {
+ // CHECKME: This is potentially called from a different thread because it is
+ // called from a timer callback. However, it does not seem to take any
+ // precautions to avoid race conditions.
int volume;
_currentVolumePercent += 10;
diff --git a/engines/saga/puzzle.cpp b/engines/saga/puzzle.cpp
index 705834f1b7..099bf79e6b 100644
--- a/engines/saga/puzzle.cpp
+++ b/engines/saga/puzzle.cpp
@@ -399,6 +399,9 @@ void Puzzle::hintTimerCallback(void *refCon) {
}
void Puzzle::solicitHint() {
+ // CHECKME: This is potentially called from a different thread because it is
+ // called from a timer callback. However, it does not seem to take any
+ // precautions to avoid race conditions.
int i;
_vm->_actor->setSpeechColor(1, kITEColorBlack);
diff --git a/engines/saga/render.cpp b/engines/saga/render.cpp
index 1f23a388d0..b932e228ad 100644
--- a/engines/saga/render.cpp
+++ b/engines/saga/render.cpp
@@ -276,6 +276,9 @@ void Render::fpsTimerCallback(void *refCon) {
}
void Render::fpsTimer() {
+ // CHECKME: This is potentially called from a different thread because it is
+ // called from a timer callback. However, it does not seem to take any
+ // precautions to avoid race conditions.
_fps = _renderedFrameCount;
_renderedFrameCount = 0;
}
diff --git a/engines/saga/saga.cpp b/engines/saga/saga.cpp
index 532b59d3c7..77a21e7f93 100644
--- a/engines/saga/saga.cpp
+++ b/engines/saga/saga.cpp
@@ -578,9 +578,11 @@ ColorId SagaEngine::KnownColor2ColorId(KnownColor knownColor) {
}
#ifdef ENABLE_IHNM
} else if (getGameId() == GID_IHNM) {
- // The default colors in the Spanish version of IHNM are shifted by one
- // Fixes bug #1848016 - "IHNM: Wrong Subtitles Color (Spanish)"
- int offset = (getLanguage() == Common::ES_ESP) ? 1 : 0;
+ // The default colors in the Spanish, version of IHNM are shifted by one
+ // Fixes bug #1848016 - "IHNM: Wrong Subtitles Color (Spanish)". This
+ // also applies to the German and French versions (bug #7064 - "IHNM:
+ // text mistake in german version").
+ int offset = (getLanguage() == Common::ES_ESP || getLanguage() == Common::DE_DEU || getLanguage() == Common::FR_FRA) ? 1 : 0;
switch (knownColor) {
case(kKnownColorTransparent):
diff --git a/engines/savestate.h b/engines/savestate.h
index 54eff0f8cb..21ade602fa 100644
--- a/engines/savestate.h
+++ b/engines/savestate.h
@@ -27,7 +27,6 @@
#include "common/str.h"
#include "common/ptr.h"
-
namespace Graphics {
struct Surface;
}
@@ -205,5 +204,13 @@ private:
/** List of savestates. */
typedef Common::Array<SaveStateDescriptor> SaveStateList;
+/**
+ * Comparator object to compare SaveStateDescriptor's based on slot.
+ */
+struct SaveStateDescriptorSlotComparator {
+ bool operator()(const SaveStateDescriptor &x, const SaveStateDescriptor &y) const {
+ return x.getSaveSlot() < y.getSaveSlot();
+ }
+};
#endif
diff --git a/engines/sci/configure.engine b/engines/sci/configure.engine
index d1c45a4654..f8a519002e 100644
--- a/engines/sci/configure.engine
+++ b/engines/sci/configure.engine
@@ -1,4 +1,4 @@
# This file is included from the main "configure" script
# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps]
add_engine sci "SCI" yes "sci32" "SCI 0-1.1 games"
-add_engine sci32 "SCI32 games" no
+add_engine sci32 "SCI32 games" no "" "" "highres"
diff --git a/engines/sci/console.cpp b/engines/sci/console.cpp
index 438c725324..a092e0676d 100644
--- a/engines/sci/console.cpp
+++ b/engines/sci/console.cpp
@@ -137,8 +137,12 @@ Console::Console(SciEngine *engine) : GUI::Debugger(),
registerCmd("wl", WRAP_METHOD(Console, cmdWindowList)); // alias
registerCmd("plane_list", WRAP_METHOD(Console, cmdPlaneList));
registerCmd("pl", WRAP_METHOD(Console, cmdPlaneList)); // alias
+ registerCmd("visible_plane_list", WRAP_METHOD(Console, cmdVisiblePlaneList));
+ registerCmd("vpl", WRAP_METHOD(Console, cmdVisiblePlaneList)); // alias
registerCmd("plane_items", WRAP_METHOD(Console, cmdPlaneItemList));
registerCmd("pi", WRAP_METHOD(Console, cmdPlaneItemList)); // alias
+ registerCmd("visible_plane_items", WRAP_METHOD(Console, cmdVisiblePlaneItemList));
+ registerCmd("vpi", WRAP_METHOD(Console, cmdVisiblePlaneItemList)); // alias
registerCmd("saved_bits", WRAP_METHOD(Console, cmdSavedBits));
registerCmd("show_saved_bits", WRAP_METHOD(Console, cmdShowSavedBits));
// Segments
@@ -190,6 +194,7 @@ Console::Console(SciEngine *engine) : GUI::Debugger(),
registerCmd("send", WRAP_METHOD(Console, cmdSend));
registerCmd("go", WRAP_METHOD(Console, cmdGo));
registerCmd("logkernel", WRAP_METHOD(Console, cmdLogKernel));
+ registerCmd("vocab994", WRAP_METHOD(Console, cmdMapVocab994));
// Breakpoints
registerCmd("bp_list", WRAP_METHOD(Console, cmdBreakpointList));
registerCmd("bplist", WRAP_METHOD(Console, cmdBreakpointList)); // alias
@@ -330,6 +335,7 @@ bool Console::cmdHelp(int argc, const char **argv) {
debugPrintf("debugflag_list - Lists the available debug flags and their status\n");
debugPrintf("debugflag_enable - Enables a debug flag\n");
debugPrintf("debugflag_disable - Disables a debug flag\n");
+ debugPrintf("debuglevel - Shows or sets debug level\n");
debugPrintf("\n");
debugPrintf("Commands\n");
debugPrintf("--------\n");
@@ -380,7 +386,9 @@ bool Console::cmdHelp(int argc, const char **argv) {
debugPrintf(" animate_list / al - Shows the current list of objects in kAnimate's draw list (SCI0 - SCI1.1)\n");
debugPrintf(" window_list / wl - Shows a list of all the windows (ports) in the draw list (SCI0 - SCI1.1)\n");
debugPrintf(" plane_list / pl - Shows a list of all the planes in the draw list (SCI2+)\n");
+ debugPrintf(" visible_plane_list / vpl - Shows a list of all the planes in the visible draw list (SCI2+)\n");
debugPrintf(" plane_items / pi - Shows a list of all items for a plane (SCI2+)\n");
+ debugPrintf(" visible_plane_items / vpi - Shows a list of all items for a plane in the visible draw list (SCI2+)\n");
debugPrintf(" saved_bits - List saved bits on the hunk\n");
debugPrintf(" show_saved_bits - Display saved bits\n");
debugPrintf("\n");
@@ -487,6 +495,9 @@ bool Console::cmdGetVersion(int argc, const char **argv) {
debugPrintf("SCI2.1 kernel table: %s\n", (_engine->_features->detectSci21KernelType() == SCI_VERSION_2) ? "modified SCI2 (old)" : "SCI2.1 (new)");
#endif
debugPrintf("View type: %s\n", viewTypeDesc[g_sci->getResMan()->getViewType()]);
+ if (getSciVersion() <= SCI_VERSION_1_1) {
+ debugPrintf("kAnimate fastCast enabled: %s\n", g_sci->_gfxAnimate->isFastCastEnabled() ? "yes" : "no");
+ }
debugPrintf("Uses palette merging: %s\n", g_sci->_gfxPalette16->isMerging() ? "yes" : "no");
debugPrintf("Uses 16 bit color matching: %s\n", g_sci->_gfxPalette16->isUsing16bitColorMatch() ? "yes" : "no");
debugPrintf("Resource volume version: %s\n", g_sci->getResMan()->getVolVersionDesc());
@@ -994,7 +1005,7 @@ bool Console::cmdHexgrep(int argc, const char **argv) {
for (; resNumber <= resMax; resNumber++) {
script = _engine->getResMan()->findResource(ResourceId(restype, resNumber), 0);
if (script) {
- unsigned int seeker = 0, seekerold = 0;
+ uint32 seeker = 0, seekerold = 0;
uint32 comppos = 0;
int output_script_name = 0;
@@ -1500,7 +1511,7 @@ bool Console::cmdSaid(int argc, const char **argv) {
}
// TODO: Maybe turn this into a proper said spec compiler
- unsigned int len = 0;
+ uint32 len = 0;
for (p++; p < argc; p++) {
if (strcmp(argv[p], ",") == 0) {
spec[len++] = 0xf0;
@@ -1537,7 +1548,7 @@ bool Console::cmdSaid(int argc, const char **argv) {
spec[len++] = 0xfe;
spec[len++] = 0xf6;
} else {
- unsigned int s = strtol(argv[p], 0, 16);
+ uint32 s = strtol(argv[p], 0, 16);
if (s >= 0xf0 && s <= 0xff) {
spec[len++] = s;
} else {
@@ -1766,6 +1777,21 @@ bool Console::cmdPlaneList(int argc, const char **argv) {
return true;
}
+bool Console::cmdVisiblePlaneList(int argc, const char **argv) {
+#ifdef ENABLE_SCI32
+ if (_engine->_gfxFrameout) {
+ debugPrintf("Visible plane list:\n");
+ _engine->_gfxFrameout->printVisiblePlaneList(this);
+ } else {
+ debugPrintf("This SCI version does not have a list of planes\n");
+ }
+#else
+ debugPrintf("SCI32 isn't included in this compiled executable\n");
+#endif
+ return true;
+}
+
+
bool Console::cmdPlaneItemList(int argc, const char **argv) {
if (argc != 2) {
debugPrintf("Shows the list of items for a plane\n");
@@ -1794,6 +1820,34 @@ bool Console::cmdPlaneItemList(int argc, const char **argv) {
return true;
}
+bool Console::cmdVisiblePlaneItemList(int argc, const char **argv) {
+ if (argc != 2) {
+ debugPrintf("Shows the list of items for a plane\n");
+ debugPrintf("Usage: %s <plane address>\n", argv[0]);
+ return true;
+ }
+
+ reg_t planeObject = NULL_REG;
+
+ if (parse_reg_t(_engine->_gamestate, argv[1], &planeObject, false)) {
+ debugPrintf("Invalid address passed.\n");
+ debugPrintf("Check the \"addresses\" command on how to use addresses\n");
+ return true;
+ }
+
+#ifdef ENABLE_SCI32
+ if (_engine->_gfxFrameout) {
+ debugPrintf("Visible plane item list:\n");
+ _engine->_gfxFrameout->printVisiblePlaneItemList(this, planeObject);
+ } else {
+ debugPrintf("This SCI version does not have a list of plane items\n");
+ }
+#else
+ debugPrintf("SCI32 isn't included in this compiled executable\n");
+#endif
+ return true;
+}
+
bool Console::cmdSavedBits(int argc, const char **argv) {
SegManager *segman = _engine->_gamestate->_segMan;
SegmentId id = segman->findSegmentByType(SEG_TYPE_HUNK);
@@ -3901,6 +3955,55 @@ bool Console::cmdSfx01Track(int argc, const char **argv) {
return true;
}
+bool Console::cmdMapVocab994(int argc, const char **argv) {
+ EngineState *s = _engine->_gamestate; // for the several defines in this function
+ reg_t reg;
+
+ if (argc != 4) {
+ debugPrintf("Attempts to map a range of vocab.994 entries to a given class\n");
+ debugPrintf("Usage: %s <class addr> <first> <last>\n", argv[0]);
+ return true;
+ }
+
+ if (parse_reg_t(_engine->_gamestate, argv[1], &reg, false)) {
+ debugPrintf("Invalid address passed.\n");
+ debugPrintf("Check the \"addresses\" command on how to use addresses\n");
+ return true;
+ }
+
+ Resource *resource = _engine->_resMan->findResource(ResourceId(kResourceTypeVocab, 994), 0);
+ const Object *obj = s->_segMan->getObject(reg);
+ uint16 *data = (uint16 *) resource->data;
+ uint32 first = atoi(argv[2]);
+ uint32 last = atoi(argv[3]);
+ Common::Array<bool> markers;
+
+ markers.resize(_engine->getKernel()->getSelectorNamesSize());
+ if (!obj->isClass() && getSciVersion() != SCI_VERSION_3)
+ obj = s->_segMan->getObject(obj->getSuperClassSelector());
+
+ first = MIN(first, (uint32) (resource->size / 2 - 2));
+ last = MIN(last, (uint32) (resource->size / 2 - 2));
+
+ for (uint32 i = first; i <= last; ++i) {
+ uint16 ofs = data[i];
+
+ if (obj && ofs < obj->getVarCount()) {
+ uint16 varSelector = obj->getVarSelector(ofs);
+ debugPrintf("%d: property at index %04x of %s is %s %s\n", i, ofs,
+ s->_segMan->derefString(obj->getNameSelector()),
+ _engine->getKernel()->getSelectorName(varSelector).c_str(),
+ markers[varSelector] ? "(repeat!)" : "");
+ markers[varSelector] = true;
+ }
+ else {
+ debugPrintf("%d: property at index %04x doesn't match up with %s\n", i, ofs,
+ s->_segMan->derefString(obj->getNameSelector()));
+ }
+ }
+
+ return true;
+}
bool Console::cmdQuit(int argc, const char **argv) {
if (argc != 2) {
}
@@ -4339,7 +4442,8 @@ int Console::printObject(reg_t pos) {
debugPrintf(" ");
if (var_container && i < var_container->getVarCount()) {
uint16 varSelector = var_container->getVarSelector(i);
- debugPrintf("[%03x] %s = ", varSelector, _engine->getKernel()->getSelectorName(varSelector).c_str());
+ // Times two commented out for now for easy parsing of vocab.994
+ debugPrintf("(%04x) [%03x] %s = ", i /* *2 */, varSelector, _engine->getKernel()->getSelectorName(varSelector).c_str());
} else
debugPrintf("p#%x = ", i);
diff --git a/engines/sci/console.h b/engines/sci/console.h
index 8b10912fbe..cf85def950 100644
--- a/engines/sci/console.h
+++ b/engines/sci/console.h
@@ -96,7 +96,9 @@ private:
bool cmdAnimateList(int argc, const char **argv);
bool cmdWindowList(int argc, const char **argv);
bool cmdPlaneList(int argc, const char **argv);
+ bool cmdVisiblePlaneList(int argc, const char **argv);
bool cmdPlaneItemList(int argc, const char **argv);
+ bool cmdVisiblePlaneItemList(int argc, const char **argv);
bool cmdSavedBits(int argc, const char **argv);
bool cmdShowSavedBits(int argc, const char **argv);
// Segments
@@ -137,6 +139,7 @@ private:
bool cmdSend(int argc, const char **argv);
bool cmdGo(int argc, const char **argv);
bool cmdLogKernel(int argc, const char **argv);
+ bool cmdMapVocab994(int argc, const char **argv);
// Breakpoints
bool cmdBreakpointList(int argc, const char **argv);
bool cmdBreakpointDelete(int argc, const char **argv);
diff --git a/engines/sci/decompressor.cpp b/engines/sci/decompressor.cpp
index e65ff148de..ca2298e67e 100644
--- a/engines/sci/decompressor.cpp
+++ b/engines/sci/decompressor.cpp
@@ -257,7 +257,7 @@ int DecompressorLZW::unpackLZW1(Common::ReadStream *src, byte *dest, uint32 nPac
init(src, dest, nPacked, nUnpacked);
byte *stak = (byte *)malloc(0x1014);
- unsigned int tokensSize = 0x1004 * sizeof(Tokenlist);
+ uint32 tokensSize = 0x1004 * sizeof(Tokenlist);
Tokenlist *tokens = (Tokenlist *)malloc(tokensSize);
if (!stak || !tokens) {
free(stak);
diff --git a/engines/sci/detection.cpp b/engines/sci/detection.cpp
index f4f104010f..c920ef10e2 100644
--- a/engines/sci/detection.cpp
+++ b/engines/sci/detection.cpp
@@ -98,8 +98,11 @@ static const PlainGameDescriptor s_sciGameTitles[] = {
{"lsl6", "Leisure Suit Larry 6: Shape Up or Slip Out!"},
{"pepper", "Pepper's Adventure in Time"},
{"slater", "Slater & Charlie Go Camping"},
+ {"gk1demo", "Gabriel Knight: Sins of the Fathers"},
+ {"qfg4demo", "Quest for Glory IV: Shadows of Darkness"},
+ {"pq4demo", "Police Quest IV: Open Season"},
// === SCI2 games =========================================================
- {"gk1", "Gabriel Knight: Sins of the Fathers"}, // demo is SCI11, full version SCI32
+ {"gk1", "Gabriel Knight: Sins of the Fathers"},
{"pq4", "Police Quest IV: Open Season"}, // floppy is SCI2, CD SCI2.1
{"qfg4", "Quest for Glory IV: Shadows of Darkness"}, // floppy is SCI2, CD SCI2.1
// === SCI2.1 games ========================================================
@@ -146,6 +149,7 @@ static const GameIdStrToEnum s_gameIdStrToEnum[] = {
{ "fairytales", GID_FAIRYTALES },
{ "freddypharkas", GID_FREDDYPHARKAS },
{ "funseeker", GID_FUNSEEKER },
+ { "gk1demo", GID_GK1DEMO },
{ "gk1", GID_GK1 },
{ "gk2", GID_GK2 },
{ "hoyle1", GID_HOYLE1 },
@@ -183,12 +187,14 @@ static const GameIdStrToEnum s_gameIdStrToEnum[] = {
{ "pq2", GID_PQ2 },
{ "pq3", GID_PQ3 },
{ "pq4", GID_PQ4 },
+ { "pq4demo", GID_PQ4DEMO },
{ "pqswat", GID_PQSWAT },
{ "qfg1", GID_QFG1 },
{ "qfg1vga", GID_QFG1VGA },
{ "qfg2", GID_QFG2 },
{ "qfg3", GID_QFG3 },
{ "qfg4", GID_QFG4 },
+ { "qfg4demo", GID_QFG4DEMO },
{ "rama", GID_RAMA },
{ "sci-fanmade", GID_FANMADE }, // FIXME: Do we really need/want this?
{ "shivers", GID_SHIVERS },
@@ -356,7 +362,7 @@ Common::String convertSierraGameId(Common::String sierraId, uint32 *gameFlags, R
// qfg4 demo has less than 50 scripts
if (resources.size() < 50)
- return "qfg4";
+ return "qfg4demo";
// Otherwise it's qfg3
return "qfg3";
@@ -473,7 +479,7 @@ static char s_fallbackGameIdBuf[256];
class SciMetaEngine : public AdvancedMetaEngine {
public:
SciMetaEngine() : AdvancedMetaEngine(Sci::SciGameDescriptions, sizeof(ADGameDescription), s_sciGameTitles, optionsList) {
- _singleid = "sci";
+ _singleId = "sci";
}
virtual const char *getName() const {
@@ -526,8 +532,8 @@ const ADGameDescription *SciMetaEngine::fallbackDetect(const FileMap &allFiles,
s_fallbackDesc.language = Common::EN_ANY;
s_fallbackDesc.flags = ADGF_NO_FLAGS;
s_fallbackDesc.platform = Common::kPlatformDOS; // default to PC platform
- s_fallbackDesc.gameid = "sci";
- s_fallbackDesc.guioptions = GUIO3(GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI);
+ s_fallbackDesc.gameId = "sci";
+ s_fallbackDesc.guiOptions = GUIO3(GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI);
if (allFiles.contains("resource.map") || allFiles.contains("Data1")
|| allFiles.contains("resmap.001") || allFiles.contains("resmap.001")) {
@@ -578,7 +584,7 @@ const ADGameDescription *SciMetaEngine::fallbackDetect(const FileMap &allFiles,
ResourceManager resMan;
resMan.addAppropriateSourcesForDetection(fslist);
- resMan.initForDetection();
+ resMan.init();
// TODO: Add error handling.
#ifndef ENABLE_SCI32
@@ -610,7 +616,7 @@ const ADGameDescription *SciMetaEngine::fallbackDetect(const FileMap &allFiles,
Common::String gameId = convertSierraGameId(sierraGameId, &s_fallbackDesc.flags, resMan);
strncpy(s_fallbackGameIdBuf, gameId.c_str(), sizeof(s_fallbackGameIdBuf) - 1);
s_fallbackGameIdBuf[sizeof(s_fallbackGameIdBuf) - 1] = 0; // Make sure string is NULL terminated
- s_fallbackDesc.gameid = s_fallbackGameIdBuf;
+ s_fallbackDesc.gameId = s_fallbackGameIdBuf;
// Try to determine the game language
// Load up text 0 and start looking for "#" characters
@@ -653,7 +659,7 @@ const ADGameDescription *SciMetaEngine::fallbackDetect(const FileMap &allFiles,
const bool isCD = (s_fallbackDesc.flags & ADGF_CD);
if (!isCD)
- s_fallbackDesc.guioptions = GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI);
+ s_fallbackDesc.guiOptions = GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI);
if (gameId.hasSuffix("sci")) {
s_fallbackDesc.extra = "SCI";
@@ -686,7 +692,7 @@ const ADGameDescription *SciMetaEngine::fallbackDetect(const FileMap &allFiles,
bool SciMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const {
const GameIdStrToEnum *g = s_gameIdStrToEnum;
for (; g->gameidStr; ++g) {
- if (0 == strcmp(desc->gameid, g->gameidStr)) {
+ if (0 == strcmp(desc->gameId, g->gameidStr)) {
*engine = new SciEngine(syst, desc, g->gameidEnum);
return true;
}
@@ -727,7 +733,6 @@ SaveStateList SciMetaEngine::listSaves(const char *target) const {
pattern += ".###";
filenames = saveFileMan->listSavefiles(pattern);
- sort(filenames.begin(), filenames.end()); // Sort (hopefully ensuring we are sorted numerically..)
SaveStateList saveList;
int slotNr = 0;
@@ -760,6 +765,8 @@ SaveStateList SciMetaEngine::listSaves(const char *target) const {
}
}
+ // Sort saves based on slot number.
+ Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator());
return saveList;
}
diff --git a/engines/sci/detection_tables.h b/engines/sci/detection_tables.h
index 25623215ed..968eb784b1 100644
--- a/engines/sci/detection_tables.h
+++ b/engines/sci/detection_tables.h
@@ -684,21 +684,23 @@ static const struct ADGameDescription SciGameDescriptions[] = {
AD_LISTEND},
Common::EN_ANY, Common::kPlatformDOS, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
- // Gabriel Knight - English DOS CD Demo
+ // Gabriel Knight - English DOS Demo
// SCI interpreter version 1.001.092
- {"gk1", "CD Demo", {
+ // Note: we are not using ADGF_DEMO here, to avoid a game ID like gk1demo-demo
+ {"gk1demo", "Demo", {
{"resource.map", 0, "39645952ae0ed8072c7e838f31b75464", 2490},
{"resource.000", 0, "eb3ed7477ca4110813fe1fcf35928561", 1718450},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformDOS, ADGF_DEMO, GUIO3(GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::EN_ANY, Common::kPlatformDOS, 0, GUIO3(GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
- // Gabriel Knight - English DOS CD Demo (from DrMcCoy)
+ // Gabriel Knight - English DOS Demo (from DrMcCoy)
// SCI interpreter version 1.001.092
- {"gk1", "CD Demo", {
+ // Note: we are not using ADGF_DEMO here, to avoid a game ID like gk1demo-demo
+ {"gk1demo", "Demo", {
{"resource.map", 0, "8cad2a256f41463030cbb7ea1bfb2857", 2490},
{"resource.000", 0, "eb3ed7477ca4110813fe1fcf35928561", 1718450},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformDOS, ADGF_DEMO, GUIO3(GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::EN_ANY, Common::kPlatformDOS, 0, GUIO3(GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
#ifdef ENABLE_SCI32
// Gabriel Knight - English DOS Floppy
@@ -941,6 +943,15 @@ static const struct ADGameDescription SciGameDescriptions[] = {
AD_LISTEND},
Common::EN_ANY, Common::kPlatformDOS, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ // Hoyle 2 - English DOS (supplied by m_kiewitz)
+ // SCI interpreter version 0.000.668, Ver 1.000.014, 2x5.25"
+ {"hoyle2", "", {
+ {"resource.map", 0, "8cef06c93d17d96f44aacd5902d84b30", 2100},
+ {"resource.001", 0, "8f2dd70abe01112eca464cda818b5eb6", 98289},
+ {"resource.002", 0, "8f2dd70abe01112eca464cda818b5eb6", 197326},
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformDOS, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+
// Hoyle 2 - English DOS (supplied by misterhands in bug report #6598)
// Game v1.000.016, interpreter 0.000.668, INT #12.5.90
{"hoyle2", "", {
@@ -1531,13 +1542,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.000", 0, "71afd220d46bde1109c58e6acc0f3a01", 469094},
{"resource.001", 0, "72a569f46f1abf2d9d2b1526ad3799c3", 12808839},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformFMTowns, 0, GUIO2(GUIO_NOASPECT, GUIO_MIDITOWNS) },
- {"kq5", "", {
- {"resource.map", 0, "20c7cd248ff1a349ed354568eebd972b", 12733},
- {"resource.000", 0, "71afd220d46bde1109c58e6acc0f3a01", 469094},
- {"resource.001", 0, "72a569f46f1abf2d9d2b1526ad3799c3", 12808839},
- AD_LISTEND},
- Common::JA_JPN, Common::kPlatformFMTowns, 0, GUIO2(GUIO_NOASPECT, GUIO_MIDITOWNS) },
+ Common::JA_JPN, Common::kPlatformFMTowns, ADGF_ADDENGLISH, GUIO3(GUIO_NOASPECT, GAMEOPTION_ORIGINAL_SAVELOAD, GUIO_MIDITOWNS) },
// King's Quest 5 - Japanese PC-98 Floppy 0.000.015 (supplied by omer_mor in bug report #3073583)
{"kq5", "", {
@@ -1646,14 +1651,6 @@ static const struct ADGameDescription SciGameDescriptions[] = {
#ifdef ENABLE_SCI32
- // King's Quest 7 - English Windows (from abevi)
- // VERSION 1.65c
- {"kq7", "", {
- {"resource.000", 0, "4948e4e1506f1e1c4e1d47abfa06b7f8", 204385195},
- {"resource.map", 0, "40ccafb2195301504eba2e4f4f2c7f3d", 18925},
- AD_LISTEND},
- Common::EN_ANY, Common::kPlatformWindows, ADGF_UNSTABLE | ADGF_CD, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
-
// King's Quest 7 - English Windows (from the King's Quest Collection)
// Executable scanning reports "2.100.002", VERSION file reports "1.4"
{"kq7", "", {
@@ -1662,6 +1659,33 @@ static const struct ADGameDescription SciGameDescriptions[] = {
AD_LISTEND},
Common::EN_ANY, Common::kPlatformWindows, ADGF_UNSTABLE | ADGF_CD, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ // King's Quest 7 - English Windows-interpreter-only (supplied by m_kiewitz)
+ // SCI interpreter version 2.100.002, VERSION file reports "1.51"
+ {"kq7", "", {
+ {"resource.map", 0, "838b9ff132bd6962026fee832e8a7ddb", 18697},
+ {"resource.000", 0, "eb63ea3a2c2469dc2d777d351c626404", 206626576},
+ {"resource.aud", 0, "c2a988a16053eb98c7b73a75139902a0", 217716879},
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformWindows, ADGF_UNSTABLE | ADGF_CD, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+
+ // King's Quest 7 - German Windows-interpreter-only (supplied by markcoolio in bug report #2727402)
+ // SCI interpreter version 2.100.002, VERSION file reports "1.51"
+ // same as English 1.51, only resource.aud/resource.sfx are different
+ {"kq7", "", {
+ {"resource.map", 0, "838b9ff132bd6962026fee832e8a7ddb", 18697},
+ {"resource.000", 0, "eb63ea3a2c2469dc2d777d351c626404", 206626576},
+ {"resource.aud", 0, "3f17bcaf8a9ff6a6c2d4de1a2078fdcc", 258119621},
+ AD_LISTEND},
+ Common::DE_DEU, Common::kPlatformWindows, ADGF_UNSTABLE | ADGF_CD, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+
+ // King's Quest 7 - English Windows (from abevi)
+ // VERSION 1.65c
+ {"kq7", "", {
+ {"resource.000", 0, "4948e4e1506f1e1c4e1d47abfa06b7f8", 204385195},
+ {"resource.map", 0, "40ccafb2195301504eba2e4f4f2c7f3d", 18925},
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformWindows, ADGF_UNSTABLE | ADGF_CD, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+
// King's Quest 7 - English DOS (from FRG)
// SCI interpreter version 2.100.002, VERSION file reports "2.00b"
{"kq7", "", {
@@ -1678,14 +1702,6 @@ static const struct ADGameDescription SciGameDescriptions[] = {
AD_LISTEND},
Common::EN_ANY, Common::kPlatformWindows, ADGF_UNSTABLE | ADGF_CD, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
- // King's Quest 7 - German Windows (supplied by markcoolio in bug report #2727402)
- // SCI interpreter version 2.100.002
- {"kq7", "", {
- {"resource.map", 0, "838b9ff132bd6962026fee832e8a7ddb", 18697},
- {"resource.000", 0, "eb63ea3a2c2469dc2d777d351c626404", 206626576},
- AD_LISTEND},
- Common::DE_DEU, Common::kPlatformDOS, ADGF_UNSTABLE | ADGF_CD, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
-
// King's Quest 7 - Spanish DOS (from jvprat)
// Executable scanning reports "2.100.002", VERSION file reports "2.00"
{"kq7", "", {
@@ -2286,6 +2302,14 @@ static const struct ADGameDescription SciGameDescriptions[] = {
AD_LISTEND},
Common::EN_ANY, Common::kPlatformDOS, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ // Larry 6 - French DOS Floppy - LOWRES (provided by theco33)
+ // SCI interpreter version 1.001.113
+ {"lsl6", "", {
+ {"resource.map", 0, "1e07144d3b06a3269236880170978acb", 6943},
+ {"resource.000", 0, "7884a8db9253e29e6b37a2651fd90ba3", 5749882},
+ AD_LISTEND},
+ Common::FR_FRA, Common::kPlatformDOS, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+
// Larry 6 - English/German/French DOS CD - LOWRES
// SCI interpreter version 1.001.115
{"lsl6", "", {
@@ -2593,12 +2617,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "b11e971ccd2040bebba59dfb409a08ef", 5772},
{"resource.001", 0, "d49625d9b8005ec01c852f8322a82867", 4330713},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformFMTowns, 0, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
- {"mothergoose256", "", {
- {"resource.map", 0, "b11e971ccd2040bebba59dfb409a08ef", 5772},
- {"resource.001", 0, "d49625d9b8005ec01c852f8322a82867", 4330713},
- AD_LISTEND},
- Common::JA_JPN, Common::kPlatformFMTowns, 0, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::JA_JPN, Common::kPlatformFMTowns, ADGF_ADDENGLISH, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
#ifdef ENABLE_SCI32
// Mixed-Up Mother Goose Deluxe - English Windows/DOS CD (supplied by markcoolio in bug report #2723810)
@@ -2627,6 +2646,28 @@ static const struct ADGameDescription SciGameDescriptions[] = {
Common::EN_ANY, Common::kPlatformDOS, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
#ifdef ENABLE_SCI32
+ // Phantasmagoria - English DOS/Windows (from csnover)
+ // Windows executable scanning reports "2.100.002" - "Aug 06 1995"
+ // DOS executable scanning reports "2.100.002" - "May 24 1995"
+ // VERSION file reports "1.000.000"
+ {"phantasmagoria", "", {
+ {"resmap.001", 0, "43c395f312a190e67b90b2c1e93a79e2", 11518},
+ {"ressci.001", 0, "3aae6559aa1df273bc542d5ac6330d75", 65844612},
+ {"resmap.002", 0, "94f142cfe8ec4107b6a42876cb603ed0", 12058},
+ {"ressci.002", 0, "3aae6559aa1df273bc542d5ac6330d75", 71588691},
+ {"resmap.003", 0, "39e9abd4501b5b6168dd07379c0be753", 12334},
+ {"ressci.003", 0, "3aae6559aa1df273bc542d5ac6330d75", 73651084},
+ {"resmap.004", 0, "434f9704658229fef322c863d2422a9a", 12556},
+ {"ressci.004", 0, "3aae6559aa1df273bc542d5ac6330d75", 75811935},
+ {"resmap.005", 0, "3ff9b4f7301800825c0ed008e091205e", 12604},
+ {"ressci.005", 0, "3aae6559aa1df273bc542d5ac6330d75", 78814934},
+ {"resmap.006", 0, "27ad413313e2a3ec3c53250e7ff5b2d1", 12532},
+ {"ressci.006", 0, "3aae6559aa1df273bc542d5ac6330d75", 77901360},
+ {"resmap.007", 0, "aa8175cfc93242af6f5e65bdceaafc0d", 7972},
+ //{"ressci.007", 0, "3aae6559aa1df273bc542d5ac6330d75", 25859038},
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformDOS, ADGF_UNSTABLE, GUIO5(GUIO_NOSPEECH, GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+
// Phantasmagoria - English DOS (from jvprat)
// Executable scanning reports "2.100.002", VERSION file reports "1.100.000UK"
{"phantasmagoria", "", {
@@ -2875,6 +2916,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
Common::EN_ANY, Common::kPlatformDOS, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Police Quest 2 - Japanese PC-98 (also includes english language)
+ // Executable scanning reports "x.yyy.zzz"
// SCI interpreter version unknown
{"pq2", "", {
{"resource.map", 0, "883804c616dca1d82373bf9fda3a71d2", 4656},
@@ -2882,7 +2924,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.002", 0, "05fdee43a228dd6ea4d1a92ccae3f788", 637662},
{"resource.003", 0, "05fdee43a228dd6ea4d1a92ccae3f788", 684395},
AD_LISTEND},
- Common::JA_JPN, Common::kPlatformPC98, ADGF_ADDENGLISH, GUIO5(GUIO_NOSPEECH, GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::JA_JPN, Common::kPlatformPC98, ADGF_ADDENGLISH, GUIO6(GUIO_NOSPEECH, GUIO_NOASPECT, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Police Quest 3 - English Amiga
// Executable scanning reports "1.004.024"
@@ -2969,11 +3011,12 @@ static const struct ADGameDescription SciGameDescriptions[] = {
// Police Quest 4 - English DOS Non-Interactive Demo (from FRG)
// SCI interpreter version 1.001.096
- {"pq4", "Demo", {
+ // Note: we are not using ADGF_DEMO here, to avoid a game ID like pq4demo-demo
+ {"pq4demo", "Demo", {
{"resource.map", 0, "be56f87a1c4a13062a30a362df860c2f", 1472},
{"resource.000", 0, "527d5684016e6816157cd15d9071b11b", 1121310},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformDOS, ADGF_DEMO, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::EN_ANY, Common::kPlatformDOS, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
#ifdef ENABLE_SCI32
// Police Quest 4 - English DOS CD (from the Police Quest Collection)
@@ -3381,11 +3424,12 @@ static const struct ADGameDescription SciGameDescriptions[] = {
// Quest for Glory 4 - English DOS Non-Interactive Demo (from FRG)
// SCI interpreter version 1.001.069 (just a guess)
- {"qfg4", "Demo", {
+ // Note: we are not using ADGF_DEMO here, to avoid a game ID like qfg4demo-demo
+ {"qfg4demo", "Demo", {
{"resource.map", 0, "1ba7c7ae1efb315326d45cb931569b1b", 922},
{"resource.000", 0, "41ba03f0b188b029132daa3ece0d3e14", 623154},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformDOS, ADGF_DEMO, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+ Common::EN_ANY, Common::kPlatformDOS, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
#ifdef ENABLE_SCI32
// Quest for Glory 4 1.1 Floppy - English DOS (supplied by markcool in bug report #2723852)
diff --git a/engines/sci/engine/kernel.cpp b/engines/sci/engine/kernel.cpp
index 0df4701334..796dea2382 100644
--- a/engines/sci/engine/kernel.cpp
+++ b/engines/sci/engine/kernel.cpp
@@ -853,7 +853,7 @@ void Kernel::loadKernelNames(GameFeatures *features) {
_kernelNames[0x26] = "Portrait";
else if (g_sci->getPlatform() == Common::kPlatformMacintosh)
_kernelNames[0x84] = "ShowMovie";
- } else if (g_sci->getGameId() == GID_QFG4 && g_sci->isDemo()) {
+ } else if (g_sci->getGameId() == GID_QFG4DEMO) {
_kernelNames[0x7b] = "RemapColors"; // QFG4 Demo has this SCI2 function instead of StrSplit
}
diff --git a/engines/sci/engine/kernel.h b/engines/sci/engine/kernel.h
index f62c43840f..62566a74b2 100644
--- a/engines/sci/engine/kernel.h
+++ b/engines/sci/engine/kernel.h
@@ -75,6 +75,10 @@ struct SciWorkaroundEntry; // from workarounds.h
* vocab.997. This results in much more readable code. Thus, this vocabulary isn't
* used at all.
*
+ * 993.voc (unneeded) - Contains the SCI3 equivalent of vocab.994; like its predecessor,
+ * the raw selector numbers can be deduced and used instead. In fact, one version of this
+ * file has turned out to cover all versiona of SCI3.
+ *
* SCI0 parser vocabularies:
* - vocab.901 / 901.voc - suffix vocabulary
* - vocab.900 / 900.voc - parse tree branches
@@ -408,7 +412,7 @@ reg_t kPlatform(EngineState *s, int argc, reg_t *argv);
reg_t kTextColors(EngineState *s, int argc, reg_t *argv);
reg_t kTextFonts(EngineState *s, int argc, reg_t *argv);
reg_t kShow(EngineState *s, int argc, reg_t *argv);
-reg_t kRemapColors(EngineState *s, int argc, reg_t *argv);
+reg_t kRemapColors16(EngineState *s, int argc, reg_t *argv);
reg_t kDummy(EngineState *s, int argc, reg_t *argv);
reg_t kEmpty(EngineState *s, int argc, reg_t *argv);
reg_t kStub(EngineState *s, int argc, reg_t *argv);
@@ -441,24 +445,54 @@ reg_t kStringLower(EngineState *s, int argc, reg_t *argv);
reg_t kStringTrn(EngineState *s, int argc, reg_t *argv);
reg_t kStringTrnExclude(EngineState *s, int argc, reg_t *argv);
+reg_t kScrollWindowCreate(EngineState *s, int argc, reg_t *argv);
+reg_t kScrollWindowAdd(EngineState *s, int argc, reg_t *argv);
+reg_t kScrollWindowWhere(EngineState *s, int argc, reg_t *argv);
+reg_t kScrollWindowShow(EngineState *s, int argc, reg_t *argv);
+reg_t kScrollWindowDestroy(EngineState *s, int argc, reg_t *argv);
+
reg_t kMulDiv(EngineState *s, int argc, reg_t *argv);
-reg_t kCantBeHere32(EngineState *s, int argc, reg_t *argv);
-reg_t kRemapColors32(EngineState *s, int argc, reg_t *argv);
-// "Screen items" in SCI32 are views
+
+reg_t kRemapColors(EngineState *s, int argc, reg_t *argv);
+reg_t kRemapOff(EngineState *s, int argc, reg_t *argv);
+reg_t kRemapByRange(EngineState *s, int argc, reg_t *argv);
+reg_t kRemapByPercent(EngineState *s, int argc, reg_t *argv);
+reg_t kRemapToGray(EngineState *s, int argc, reg_t *argv);
+reg_t kRemapToPercentGray(EngineState *s, int argc, reg_t *argv);
+reg_t kRemapSetNoMatchRange(EngineState *s, int argc, reg_t *argv);
+
reg_t kAddScreenItem(EngineState *s, int argc, reg_t *argv);
reg_t kUpdateScreenItem(EngineState *s, int argc, reg_t *argv);
reg_t kDeleteScreenItem(EngineState *s, int argc, reg_t *argv);
-// Text
+
reg_t kCreateTextBitmap(EngineState *s, int argc, reg_t *argv);
-reg_t kDisposeTextBitmap(EngineState *s, int argc, reg_t *argv);
-// "Planes" in SCI32 are pictures
+reg_t kBitmap(EngineState *s, int argc, reg_t *argv);
+reg_t kBitmapCreate(EngineState *s, int argc, reg_t *argv);
+reg_t kBitmapDestroy(EngineState *s, int argc, reg_t *argv);
+reg_t kBitmapDrawLine(EngineState *s, int argc, reg_t *argv);
+reg_t kBitmapDrawView(EngineState *s, int argc, reg_t *argv);
+reg_t kBitmapDrawText(EngineState *s, int argc, reg_t *argv);
+reg_t kBitmapDrawColor(EngineState *s, int argc, reg_t *argv);
+reg_t kBitmapDrawBitmap(EngineState *s, int argc, reg_t *argv);
+reg_t kBitmapInvert(EngineState *s, int argc, reg_t *argv);
+reg_t kBitmapSetDisplace(EngineState *s, int argc, reg_t *argv);
+reg_t kBitmapCreateFromView(EngineState *s, int argc, reg_t *argv);
+reg_t kBitmapCopyPixels(EngineState *s, int argc, reg_t *argv);
+reg_t kBitmapClone(EngineState *s, int argc, reg_t *argv);
+reg_t kBitmapGetInfo(EngineState *s, int argc, reg_t *argv);
+reg_t kBitmapScale(EngineState *s, int argc, reg_t *argv);
+reg_t kBitmapCreateFromUnknown(EngineState *s, int argc, reg_t *argv);
+
reg_t kAddPlane(EngineState *s, int argc, reg_t *argv);
reg_t kDeletePlane(EngineState *s, int argc, reg_t *argv);
reg_t kUpdatePlane(EngineState *s, int argc, reg_t *argv);
+reg_t kMovePlaneItems(EngineState *s, int argc, reg_t *argv);
reg_t kSetShowStyle(EngineState *s, int argc, reg_t *argv);
reg_t kSetPalStyleRange(EngineState *s, int argc, reg_t *argv);
reg_t kGetHighPlanePri(EngineState *s, int argc, reg_t *argv);
reg_t kFrameOut(EngineState *s, int argc, reg_t *argv);
+reg_t kCelHigh32(EngineState *s, int argc, reg_t *argv);
+reg_t kCelWide32(EngineState *s, int argc, reg_t *argv);
reg_t kIsOnMe(EngineState *s, int argc, reg_t *argv); // kOnMe for SCI2, kIsOnMe for SCI2.1
reg_t kInPolygon(EngineState *s, int argc, reg_t *argv);
@@ -473,6 +507,7 @@ reg_t kEditText(EngineState *s, int argc, reg_t *argv);
reg_t kMakeSaveCatName(EngineState *s, int argc, reg_t *argv);
reg_t kMakeSaveFileName(EngineState *s, int argc, reg_t *argv);
reg_t kSetScroll(EngineState *s, int argc, reg_t *argv);
+
reg_t kPalCycle(EngineState *s, int argc, reg_t *argv);
reg_t kPaletteSetFade(EngineState *s, int argc, reg_t *argv);
reg_t kPalVarySetVary(EngineState *s, int argc, reg_t *argv);
@@ -488,6 +523,8 @@ reg_t kPalVaryMergeStart(EngineState *s, int argc, reg_t *argv);
// SCI2.1 Kernel Functions
reg_t kMorphOn(EngineState *s, int argc, reg_t *argv);
reg_t kText(EngineState *s, int argc, reg_t *argv);
+reg_t kTextSize32(EngineState *s, int argc, reg_t *argv);
+reg_t kTextWidth(EngineState *s, int argc, reg_t *argv);
reg_t kSave(EngineState *s, int argc, reg_t *argv);
reg_t kAutoSave(EngineState *s, int argc, reg_t *argv);
reg_t kList(EngineState *s, int argc, reg_t *argv);
@@ -505,9 +542,9 @@ reg_t kGetSierraProfileInt(EngineState *s, int argc, reg_t *argv);
reg_t kCelInfo(EngineState *s, int argc, reg_t *argv);
reg_t kSetLanguage(EngineState *s, int argc, reg_t *argv);
reg_t kScrollWindow(EngineState *s, int argc, reg_t *argv);
+reg_t kSetFontHeight(EngineState *s, int argc, reg_t *argv);
reg_t kSetFontRes(EngineState *s, int argc, reg_t *argv);
reg_t kFont(EngineState *s, int argc, reg_t *argv);
-reg_t kBitmap(EngineState *s, int argc, reg_t *argv);
reg_t kAddLine(EngineState *s, int argc, reg_t *argv);
reg_t kUpdateLine(EngineState *s, int argc, reg_t *argv);
reg_t kDeleteLine(EngineState *s, int argc, reg_t *argv);
diff --git a/engines/sci/engine/kernel_tables.h b/engines/sci/engine/kernel_tables.h
index fce3230a18..3463d05e77 100644
--- a/engines/sci/engine/kernel_tables.h
+++ b/engines/sci/engine/kernel_tables.h
@@ -60,14 +60,19 @@ struct SciKernelMapSubEntry {
#define SCI_SUBOPENTRY_TERMINATOR { SCI_VERSION_NONE, SCI_VERSION_NONE, 0, NULL, NULL, NULL, NULL }
-#define SIG_SCIALL SCI_VERSION_NONE, SCI_VERSION_NONE
-#define SIG_SCI0 SCI_VERSION_NONE, SCI_VERSION_01
-#define SIG_SCI1 SCI_VERSION_1_EGA_ONLY, SCI_VERSION_1_LATE
-#define SIG_SCI11 SCI_VERSION_1_1, SCI_VERSION_1_1
-#define SIG_SINCE_SCI11 SCI_VERSION_1_1, SCI_VERSION_NONE
-#define SIG_SINCE_SCI21 SCI_VERSION_2_1_EARLY, SCI_VERSION_3
-#define SIG_UNTIL_SCI21MID SCI_VERSION_2, SCI_VERSION_2_1_MIDDLE
-#define SIG_SINCE_SCI21LATE SCI_VERSION_2_1_LATE, SCI_VERSION_3
+#define SIG_SCIALL SCI_VERSION_NONE, SCI_VERSION_NONE
+#define SIG_SCI0 SCI_VERSION_NONE, SCI_VERSION_01
+#define SIG_SCI1 SCI_VERSION_1_EGA_ONLY, SCI_VERSION_1_LATE
+#define SIG_SCI11 SCI_VERSION_1_1, SCI_VERSION_1_1
+#define SIG_SINCE_SCI11 SCI_VERSION_1_1, SCI_VERSION_NONE
+#define SIG_SCI2 SCI_VERSION_2, SCI_VERSION_2
+#define SIG_SCI21EARLY SCI_VERSION_2_1_EARLY, SCI_VERSION_2_1_EARLY
+#define SIG_UNTIL_SCI21EARLY SCI_VERSION_2, SCI_VERSION_2_1_EARLY
+#define SIG_UNTIL_SCI21MID SCI_VERSION_2, SCI_VERSION_2_1_MIDDLE
+#define SIG_SINCE_SCI21 SCI_VERSION_2_1_EARLY, SCI_VERSION_3
+#define SIG_SINCE_SCI21MID SCI_VERSION_2_1_MIDDLE, SCI_VERSION_3
+#define SIG_SINCE_SCI21LATE SCI_VERSION_2_1_LATE, SCI_VERSION_3
+#define SIG_SCI3 SCI_VERSION_3, SCI_VERSION_3
#define SIG_SCI16 SCI_VERSION_NONE, SCI_VERSION_1_1
#define SIG_SCI32 SCI_VERSION_2, SCI_VERSION_NONE
@@ -210,7 +215,7 @@ static const SciKernelMapSubEntry kPalVary_subops[] = {
{ SIG_SCI16, 6, MAP_CALL(PalVaryPauseResume), "i", NULL },
#ifdef ENABLE_SCI32
{ SIG_SCI32, 0, MAP_CALL(PalVarySetVary), "i(i)(i)(ii)", NULL },
- { SIG_SCI32, 1, MAP_CALL(PalVarySetPercent), "(i)(i)", NULL },
+ { SIG_SCI32, 1, MAP_CALL(PalVarySetPercent), "(i)(i)", kPalVarySetPercent_workarounds },
{ SIG_SCI32, 2, MAP_CALL(PalVaryGetPercent), "", NULL },
{ SIG_SCI32, 3, MAP_CALL(PalVaryOff), "", NULL },
{ SIG_SCI32, 4, MAP_CALL(PalVaryMergeTarget), "i", NULL },
@@ -283,6 +288,42 @@ static const SciKernelMapSubEntry kSave_subops[] = {
};
// version, subId, function-mapping, signature, workarounds
+static const SciKernelMapSubEntry kFont_subops[] = {
+ { SIG_SINCE_SCI21MID, 0, MAP_CALL(SetFontHeight), "i", NULL },
+ { SIG_SINCE_SCI21MID, 1, MAP_CALL(SetFontRes), "ii", NULL },
+ SCI_SUBOPENTRY_TERMINATOR
+};
+
+// version, subId, function-mapping, signature, workarounds
+static const SciKernelMapSubEntry kText_subops[] = {
+ { SIG_SINCE_SCI21MID, 0, MAP_CALL(TextSize32), "r[r0]i(i)(i)", NULL },
+ { SIG_SINCE_SCI21MID, 1, MAP_CALL(TextWidth), "ri", NULL },
+ SCI_SUBOPENTRY_TERMINATOR
+};
+
+// version, subId, function-mapping, signature, workarounds
+static const SciKernelMapSubEntry kBitmap_subops[] = {
+ { SIG_SINCE_SCI21, 0, MAP_CALL(BitmapCreate), "iiii(i)(i)(i)", NULL },
+ { SIG_SINCE_SCI21, 1, MAP_CALL(BitmapDestroy), "r", NULL },
+ { SIG_SINCE_SCI21, 2, MAP_CALL(BitmapDrawLine), "riiiii(i)(i)", NULL },
+ { SIG_SINCE_SCI21, 3, MAP_CALL(BitmapDrawView), "riii(i)(i)(0)(i)(i)", NULL },
+ { SIG_SINCE_SCI21, 4, MAP_CALL(BitmapDrawText), "rriiiiiiiiiii", NULL },
+ { SIG_SINCE_SCI21, 5, MAP_CALL(BitmapDrawColor), "riiiii", NULL },
+ { SIG_SINCE_SCI21, 6, MAP_CALL(BitmapDrawBitmap), "rr(i)(i)(i)", NULL },
+ { SIG_SINCE_SCI21, 7, MAP_CALL(BitmapInvert), "riiiiii", NULL },
+ { SIG_SINCE_SCI21MID, 8, MAP_CALL(BitmapSetDisplace), "rii", NULL },
+ { SIG_SINCE_SCI21MID, 9, MAP_CALL(BitmapCreateFromView), "iii(i)(i)(i)([r0])", NULL },
+ { SIG_SINCE_SCI21MID, 10, MAP_CALL(BitmapCopyPixels), "rr", NULL },
+ { SIG_SINCE_SCI21MID, 11, MAP_CALL(BitmapClone), "r", NULL },
+ { SIG_SINCE_SCI21LATE, 12, MAP_CALL(BitmapGetInfo), "r(i)(i)", NULL },
+ { SIG_SINCE_SCI21LATE, 13, MAP_CALL(BitmapScale), "r...ii", NULL },
+ { SIG_SCI3, 14, MAP_CALL(BitmapCreateFromUnknown), "......", NULL },
+ { SIG_SCI3, 15, MAP_EMPTY(Bitmap), "(.*)", NULL },
+ { SIG_SCI3, 16, MAP_EMPTY(Bitmap), "(.*)", NULL },
+ SCI_SUBOPENTRY_TERMINATOR
+};
+
+// version, subId, function-mapping, signature, workarounds
static const SciKernelMapSubEntry kList_subops[] = {
{ SIG_SINCE_SCI21, 0, MAP_CALL(NewList), "", NULL },
{ SIG_SINCE_SCI21, 1, MAP_CALL(DisposeList), "l", NULL },
@@ -311,6 +352,17 @@ static const SciKernelMapSubEntry kList_subops[] = {
};
// version, subId, function-mapping, signature, workarounds
+static const SciKernelMapSubEntry kRemapColors_subops[] = {
+ { SIG_SCI32, 0, MAP_CALL(RemapOff), "(i)", NULL },
+ { SIG_SCI32, 1, MAP_CALL(RemapByRange), "iiii(i)", NULL },
+ { SIG_SCI32, 2, MAP_CALL(RemapByPercent), "ii(i)", NULL },
+ { SIG_SCI32, 3, MAP_CALL(RemapToGray), "ii(i)", NULL },
+ { SIG_SCI32, 4, MAP_CALL(RemapToPercentGray), "iii(i)", NULL },
+ { SIG_SCI32, 5, MAP_CALL(RemapSetNoMatchRange), "ii", NULL },
+ SCI_SUBOPENTRY_TERMINATOR
+};
+
+// version, subId, function-mapping, signature, workarounds
static const SciKernelMapSubEntry kString_subops[] = {
{ SIG_SCI32, 0, MAP_CALL(StringNew), "i(i)", NULL },
{ SIG_SCI32, 1, MAP_CALL(StringSize), "[or]", NULL },
@@ -352,6 +404,31 @@ static const SciKernelMapSubEntry kString_subops[] = {
SCI_SUBOPENTRY_TERMINATOR
};
+// version, subId, function-mapping, signature, workarounds
+static const SciKernelMapSubEntry kScrollWindow_subops[] = {
+ { SIG_SCI32, 0, MAP_CALL(ScrollWindowCreate), "oi", NULL },
+ { SIG_SCI32, 1, MAP_CALL(ScrollWindowAdd), "o.ii.(.)", NULL },
+ { SIG_SCI32, 2, MAP_DUMMY(ScrollWindowClear), "o", NULL },
+ { SIG_SCI32, 3, MAP_DUMMY(ScrollWindowPageUp), "o", NULL },
+ { SIG_SCI32, 4, MAP_DUMMY(ScrollWindowPageDown), "o", NULL },
+ { SIG_SCI32, 5, MAP_DUMMY(ScrollWindowUpArrow), "o", NULL },
+ { SIG_SCI32, 6, MAP_DUMMY(ScrollWindowDownArrow), "o", NULL },
+ { SIG_SCI32, 7, MAP_DUMMY(ScrollWindowHome), "o", NULL },
+ { SIG_SCI32, 8, MAP_DUMMY(ScrollWindowEnd), "o", NULL },
+ { SIG_SCI32, 9, MAP_DUMMY(ScrollWindowResize), "o.", NULL },
+ { SIG_SCI32, 10, MAP_CALL(ScrollWindowWhere), "oi", NULL },
+ { SIG_SCI32, 11, MAP_DUMMY(ScrollWindowGo), "o..", NULL },
+ { SIG_SCI32, 12, MAP_DUMMY(ScrollWindowInsert), "o.....", NULL },
+ { SIG_SCI32, 13, MAP_DUMMY(ScrollWindowDelete), "o.", NULL },
+ { SIG_SCI32, 14, MAP_DUMMY(ScrollWindowModify), "o.....(.)", NULL },
+ { SIG_SCI32, 15, MAP_DUMMY(ScrollWindowHide), "o", NULL },
+ { SIG_SCI32, 16, MAP_CALL(ScrollWindowShow), "o", NULL },
+ { SIG_SCI32, 17, MAP_CALL(ScrollWindowDestroy), "o", NULL },
+ { SIG_SCI32, 18, MAP_DUMMY(ScrollWindowText), "o", NULL },
+ { SIG_SCI32, 19, MAP_DUMMY(ScrollWindowReconstruct), "o.", NULL },
+ SCI_SUBOPENTRY_TERMINATOR
+};
+
#endif
struct SciKernelMapEntry {
@@ -380,12 +457,16 @@ static SciKernelMapEntry s_kernelMap[] = {
{ MAP_CALL(AvoidPath), SIG_EVERYWHERE, "ii(.*)", NULL, NULL },
{ MAP_CALL(BaseSetter), SIG_EVERYWHERE, "o", NULL, NULL },
{ MAP_CALL(CanBeHere), SIG_EVERYWHERE, "o(l)", NULL, NULL },
+ { MAP_CALL(CantBeHere), SIG_SCI16, SIGFOR_ALL, "o(l)", NULL, NULL },
#ifdef ENABLE_SCI32
- { "CantBeHere", kCantBeHere32, SIG_SCI32, SIGFOR_ALL, "ol", NULL, NULL },
+ { MAP_CALL(CantBeHere), SIG_SCI32, SIGFOR_ALL, "ol", NULL, NULL },
+#endif
+ { MAP_CALL(CelHigh), SIG_SCI16, SIGFOR_ALL, "ii(i)", NULL, kCelHigh_workarounds },
+ { MAP_CALL(CelWide), SIG_SCI16, SIGFOR_ALL, "ii(i)", NULL, kCelWide_workarounds },
+#ifdef ENABLE_SCI32
+ { "CelHigh", kCelHigh32, SIG_SCI32, SIGFOR_ALL, "iii", NULL, NULL },
+ { "CelWide", kCelWide32, SIG_SCI32, SIGFOR_ALL, "iii", NULL, NULL },
#endif
- { MAP_CALL(CantBeHere), SIG_EVERYWHERE, "o(l)", NULL, NULL },
- { MAP_CALL(CelHigh), SIG_EVERYWHERE, "ii(i)", NULL, kCelHigh_workarounds },
- { MAP_CALL(CelWide), SIG_EVERYWHERE, "ii(i)", NULL, kCelWide_workarounds },
{ MAP_CALL(CheckFreeSpace), SIG_SCI32, SIGFOR_ALL, "r.*", NULL, NULL },
{ MAP_CALL(CheckFreeSpace), SIG_SCI11, SIGFOR_ALL, "r(i)", NULL, NULL },
{ MAP_CALL(CheckFreeSpace), SIG_EVERYWHERE, "r", NULL, NULL },
@@ -484,9 +565,9 @@ static SciKernelMapEntry s_kernelMap[] = {
{ MAP_CALL(PriCoord), SIG_EVERYWHERE, "i", NULL, NULL },
{ MAP_CALL(Random), SIG_EVERYWHERE, "i(i)(i)", NULL, NULL },
{ MAP_CALL(ReadNumber), SIG_EVERYWHERE, "r", NULL, kReadNumber_workarounds },
- { MAP_CALL(RemapColors), SIG_SCI11, SIGFOR_ALL, "i(i)(i)(i)(i)", NULL, NULL },
+ { "RemapColors", kRemapColors16, SIG_SCI11, SIGFOR_ALL, "i(i)(i)(i)(i)", NULL, NULL },
#ifdef ENABLE_SCI32
- { "RemapColors", kRemapColors32, SIG_SCI32, SIGFOR_ALL, "i(i)(i)(i)(i)(i)", NULL, NULL },
+ { MAP_CALL(RemapColors), SIG_SCI32, SIGFOR_ALL, "i(i)(i)(i)(i)(i)", kRemapColors_subops, NULL },
#endif
{ MAP_CALL(ResCheck), SIG_EVERYWHERE, "ii(iiii)", NULL, NULL },
{ MAP_CALL(RespondsTo), SIG_EVERYWHERE, ".i", NULL, NULL },
@@ -502,7 +583,10 @@ static SciKernelMapEntry s_kernelMap[] = {
{ MAP_CALL(SetDebug), SIG_EVERYWHERE, "(i*)", NULL, NULL },
{ MAP_CALL(SetJump), SIG_EVERYWHERE, "oiii", NULL, NULL },
{ MAP_CALL(SetMenu), SIG_EVERYWHERE, "i(.*)", NULL, NULL },
- { MAP_CALL(SetNowSeen), SIG_EVERYWHERE, "o(i)", NULL, NULL },
+ { MAP_CALL(SetNowSeen), SIG_SCI16, SIGFOR_ALL, "o(i)", NULL, NULL },
+#ifdef ENABLE_SCI32
+ { MAP_CALL(SetNowSeen), SIG_SCI32, SIGFOR_ALL, "o", NULL, NULL },
+#endif
{ MAP_CALL(SetPort), SIG_EVERYWHERE, "i(iiiii)(i)", NULL, kSetPort_workarounds },
{ MAP_CALL(SetQuitStr), SIG_EVERYWHERE, "r", NULL, NULL },
{ MAP_CALL(SetSynonyms), SIG_EVERYWHERE, "o", NULL, NULL },
@@ -520,10 +604,10 @@ static SciKernelMapEntry s_kernelMap[] = {
{ MAP_CALL(StrEnd), SIG_EVERYWHERE, "r", NULL, NULL },
{ MAP_CALL(StrLen), SIG_EVERYWHERE, "[r0]", NULL, kStrLen_workarounds },
{ MAP_CALL(StrSplit), SIG_EVERYWHERE, "rr[r0]", NULL, NULL },
- { MAP_CALL(TextColors), SIG_EVERYWHERE, "(i*)", NULL, NULL },
- { MAP_CALL(TextFonts), SIG_EVERYWHERE, "(i*)", NULL, NULL },
- { MAP_CALL(TextSize), SIG_SCIALL, SIGFOR_MAC, "r[r0]i(i)(r0)(i)", NULL, NULL },
- { MAP_CALL(TextSize), SIG_EVERYWHERE, "r[r0]i(i)(r0)", NULL, NULL },
+ { MAP_CALL(TextColors), SIG_SCI16, SIGFOR_ALL, "(i*)", NULL, NULL },
+ { MAP_CALL(TextFonts), SIG_SCI16, SIGFOR_ALL, "(i*)", NULL, NULL },
+ { MAP_CALL(TextSize), SIG_SCI16, SIGFOR_MAC, "r[r0]i(i)(r0)(i)", NULL, NULL },
+ { MAP_CALL(TextSize), SIG_SCI16, SIGFOR_ALL, "r[r0]i(i)(r0)", NULL, NULL },
{ MAP_CALL(TimesCos), SIG_EVERYWHERE, "ii", NULL, NULL },
{ "CosMult", kTimesCos, SIG_EVERYWHERE, "ii", NULL, NULL },
{ MAP_CALL(TimesCot), SIG_EVERYWHERE, "ii", NULL, NULL },
@@ -555,14 +639,18 @@ static SciKernelMapEntry s_kernelMap[] = {
#ifdef ENABLE_SCI32
// SCI2 Kernel Functions
// TODO: whoever knows his way through those calls, fix the signatures.
+ { "TextSize", kTextSize32, SIG_UNTIL_SCI21EARLY, SIGFOR_ALL, "r[r0]i(i)", NULL, NULL },
+ { MAP_DUMMY(TextColors), SIG_UNTIL_SCI21EARLY, SIGFOR_ALL, "(.*)", NULL, NULL },
+ { MAP_DUMMY(TextFonts), SIG_UNTIL_SCI21EARLY, SIGFOR_ALL, "(.*)", NULL, NULL },
+
{ MAP_CALL(AddPlane), SIG_EVERYWHERE, "o", NULL, NULL },
{ MAP_CALL(AddScreenItem), SIG_EVERYWHERE, "o", NULL, NULL },
{ MAP_CALL(Array), SIG_EVERYWHERE, "(.*)", NULL, NULL },
{ MAP_CALL(CreateTextBitmap), SIG_EVERYWHERE, "i(.*)", NULL, NULL },
{ MAP_CALL(DeletePlane), SIG_EVERYWHERE, "o", NULL, NULL },
{ MAP_CALL(DeleteScreenItem), SIG_EVERYWHERE, "o", NULL, NULL },
- { MAP_CALL(DisposeTextBitmap), SIG_EVERYWHERE, "r", NULL, NULL },
- { MAP_CALL(FrameOut), SIG_EVERYWHERE, "", NULL, NULL },
+ { "DisposeTextBitmap", kBitmapDestroy, SIG_SCI2, SIGFOR_ALL, "r", NULL, NULL },
+ { MAP_CALL(FrameOut), SIG_EVERYWHERE, "(i)", NULL, NULL },
{ MAP_CALL(GetHighPlanePri), SIG_EVERYWHERE, "", NULL, NULL },
{ MAP_CALL(InPolygon), SIG_EVERYWHERE, "iio", NULL, NULL },
{ MAP_CALL(IsHiRes), SIG_EVERYWHERE, "", NULL, NULL },
@@ -623,9 +711,9 @@ static SciKernelMapEntry s_kernelMap[] = {
{ MAP_DUMMY(MarkMemory), SIG_EVERYWHERE, "(.*)", NULL, NULL },
{ MAP_DUMMY(GetHighItemPri), SIG_EVERYWHERE, "(.*)", NULL, NULL },
{ MAP_DUMMY(ShowStylePercent), SIG_EVERYWHERE, "(.*)", NULL, NULL },
- { MAP_DUMMY(InvertRect), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_DUMMY(InvertRect), SIG_UNTIL_SCI21EARLY, SIGFOR_ALL, "(.*)", NULL, NULL },
{ MAP_DUMMY(InputText), SIG_EVERYWHERE, "(.*)", NULL, NULL },
- { MAP_DUMMY(TextWidth), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_CALL(TextWidth), SIG_UNTIL_SCI21EARLY, SIGFOR_ALL, "ri", NULL, NULL },
{ MAP_DUMMY(PointSize), SIG_EVERYWHERE, "(.*)", NULL, NULL },
// SCI2.1 Kernel Functions
@@ -636,18 +724,18 @@ static SciKernelMapEntry s_kernelMap[] = {
{ MAP_CALL(PlayVMD), SIG_EVERYWHERE, "(.*)", NULL, NULL },
{ MAP_CALL(Robot), SIG_EVERYWHERE, "(.*)", NULL, NULL },
{ MAP_CALL(Save), SIG_EVERYWHERE, "i(.*)", kSave_subops, NULL },
- { MAP_CALL(Text), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_CALL(Text), SIG_SINCE_SCI21MID, SIGFOR_ALL, "i(.*)", kText_subops, NULL },
{ MAP_CALL(AddPicAt), SIG_EVERYWHERE, "oiii", NULL, NULL },
{ MAP_CALL(GetWindowsOption), SIG_EVERYWHERE, "i", NULL, NULL },
{ MAP_CALL(WinHelp), SIG_EVERYWHERE, "(.*)", NULL, NULL },
{ MAP_CALL(GetConfig), SIG_EVERYWHERE, "ro", NULL, NULL },
{ MAP_CALL(GetSierraProfileInt), SIG_EVERYWHERE, "rri", NULL, NULL },
- { MAP_CALL(CelInfo), SIG_EVERYWHERE, "iiiiii", NULL, NULL },
- { MAP_CALL(SetLanguage), SIG_EVERYWHERE, "r", NULL, NULL },
- { MAP_CALL(ScrollWindow), SIG_EVERYWHERE, "io(.*)", NULL, NULL },
- { MAP_CALL(SetFontRes), SIG_EVERYWHERE, "ii", NULL, NULL },
- { MAP_CALL(Font), SIG_EVERYWHERE, "i(.*)", NULL, NULL },
- { MAP_CALL(Bitmap), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_CALL(CelInfo), SIG_SINCE_SCI21MID, SIGFOR_ALL, "iiiiii", NULL, NULL },
+ { MAP_CALL(SetLanguage), SIG_SINCE_SCI21MID, SIGFOR_ALL, "r", NULL, NULL },
+ { MAP_CALL(ScrollWindow), SIG_EVERYWHERE, "i(.*)", kScrollWindow_subops, NULL },
+ { MAP_CALL(SetFontRes), SIG_SCI21EARLY, SIGFOR_ALL, "ii", NULL, NULL },
+ { MAP_CALL(Font), SIG_SINCE_SCI21MID, SIGFOR_ALL, "i(.*)", kFont_subops, NULL },
+ { MAP_CALL(Bitmap), SIG_EVERYWHERE, "(.*)", kBitmap_subops, NULL },
{ MAP_CALL(AddLine), SIG_EVERYWHERE, "oiiiiiiiii", NULL, NULL },
{ MAP_CALL(UpdateLine), SIG_EVERYWHERE, "[r0]oiiiiiiiii", NULL, NULL },
{ MAP_CALL(DeleteLine), SIG_EVERYWHERE, "[r0]o", NULL, NULL },
@@ -699,9 +787,9 @@ static SciKernelMapEntry s_kernelMap[] = {
// <lskovlun> The idea, if I understand correctly, is that the engine generates events
// of a special HotRect type continuously when the mouse is on that rectangle
- // MovePlaneItems - used by SQ6 to scroll through the inventory via the up/down buttons
- // SetPalStyleRange - 2 integer parameters, start and end. All styles from start-end
- // (inclusive) are set to 0
+ // Used by SQ6 to scroll through the inventory via the up/down buttons
+ { MAP_CALL(MovePlaneItems), SIG_SINCE_SCI21, SIGFOR_ALL, "oii(i)", NULL, NULL },
+
{ MAP_CALL(SetPalStyleRange), SIG_EVERYWHERE, "ii", NULL, NULL },
{ MAP_CALL(MorphOn), SIG_EVERYWHERE, "", NULL, NULL },
@@ -1004,7 +1092,6 @@ static const char *const sci2_default_knames[] = {
/*0x89*/ "TextWidth", // for debugging(?), only in SCI2, not used in any SCI2 game
/*0x8a*/ "PointSize", // for debugging(?), only in SCI2, not used in any SCI2 game
- // GK2 Demo (and similar) only kernel functions
/*0x8b*/ "AddLine",
/*0x8c*/ "DeleteLine",
/*0x8d*/ "UpdateLine",
diff --git a/engines/sci/engine/kevent.cpp b/engines/sci/engine/kevent.cpp
index bb595e9960..534d9ce713 100644
--- a/engines/sci/engine/kevent.cpp
+++ b/engines/sci/engine/kevent.cpp
@@ -83,11 +83,12 @@ reg_t kGetEvent(EngineState *s, int argc, reg_t *argv) {
}
// For a real event we use its associated mouse position
- mousePos = curEvent.mousePos;
#ifdef ENABLE_SCI32
- if (getSciVersion() >= SCI_VERSION_2_1_EARLY)
- g_sci->_gfxCoordAdjuster->fromDisplayToScript(mousePos.y, mousePos.x);
+ if (getSciVersion() >= SCI_VERSION_2)
+ mousePos = curEvent.mousePosSci;
+ else
#endif
+ mousePos = curEvent.mousePos;
// Limit the mouse cursor position, if necessary
g_sci->_gfxCursor->refreshPosition();
@@ -101,7 +102,25 @@ reg_t kGetEvent(EngineState *s, int argc, reg_t *argv) {
// question. Check GfxCursor::setPosition(), for a more detailed
// explanation and a list of cursor position workarounds.
if (s->_cursorWorkaroundRect.contains(mousePos.x, mousePos.y)) {
- s->_cursorWorkaroundActive = false;
+ // For OpenPandora and possibly other platforms, that support analog-stick control + touch screen
+ // control at the same time: in case the cursor is currently at the coordinate set by the scripts,
+ // we will count down instead of immediately disabling the workaround.
+ // On OpenPandora the cursor position is set, but it's overwritten shortly afterwards by the
+ // touch screen. In this case we would sometimes disable the workaround, simply because the touch
+ // screen hasn't yet overwritten the position and thus the workaround would not work anymore.
+ // On OpenPandora it would sometimes work and sometimes not without this.
+ if (s->_cursorWorkaroundPoint == mousePos) {
+ // Cursor is still at the same spot as set by the scripts
+ if (s->_cursorWorkaroundPosCount > 0) {
+ s->_cursorWorkaroundPosCount--;
+ } else {
+ // Was for quite a bit of time at that spot, so disable workaround now
+ s->_cursorWorkaroundActive = false;
+ }
+ } else {
+ // Cursor has moved, but is within the rect -> disable workaround immediately
+ s->_cursorWorkaroundActive = false;
+ }
} else {
mousePos.x = s->_cursorWorkaroundPoint.x;
mousePos.y = s->_cursorWorkaroundPoint.y;
diff --git a/engines/sci/engine/kfile.cpp b/engines/sci/engine/kfile.cpp
index 979fa95a42..335763a35f 100644
--- a/engines/sci/engine/kfile.cpp
+++ b/engines/sci/engine/kfile.cpp
@@ -37,7 +37,6 @@
#include "sci/engine/state.h"
#include "sci/engine/kernel.h"
#include "sci/engine/savegame.h"
-#include "sci/graphics/menu.h"
#include "sci/sound/audio.h"
#include "sci/console.h"
@@ -602,6 +601,16 @@ reg_t kFileIOExists(EngineState *s, int argc, reg_t *argv) {
bool exists = false;
+ if (g_sci->getGameId() == GID_PEPPER) {
+ // HACK: Special case for Pepper's Adventure in Time
+ // The game checks like crazy for the file CDAUDIO when entering the game menu.
+ // On at least Windows that makes the engine slow down to a crawl and takes at least 1 second.
+ // Should get solved properly by changing the code below. This here is basically for 1.8.0 release.
+ // TODO: Fix this properly.
+ if (name == "CDAUDIO")
+ return NULL_REG;
+ }
+
// Check for regular file
exists = Common::File::exists(name);
@@ -907,50 +916,8 @@ reg_t kRestoreGame(EngineState *s, int argc, reg_t *argv) {
gamestate_restore(s, in);
delete in;
- switch (g_sci->getGameId()) {
- case GID_MOTHERGOOSE:
- // WORKAROUND: Mother Goose SCI0
- // Script 200 / rm200::newRoom will set global C5h directly right after creating a child to the
- // current number of children plus 1.
- // We can't trust that global, that's why we set the actual savedgame id right here directly after
- // restoring a saved game.
- // If we didn't, the game would always save to a new slot
- s->variables[VAR_GLOBAL][0xC5].setOffset(SAVEGAMEID_OFFICIALRANGE_START + savegameId);
- break;
- case GID_MOTHERGOOSE256:
- // WORKAROUND: Mother Goose SCI1/SCI1.1 does some weird things for
- // saving a previously restored game.
- // We set the current savedgame-id directly and remove the script
- // code concerning this via script patch.
- s->variables[VAR_GLOBAL][0xB3].setOffset(SAVEGAMEID_OFFICIALRANGE_START + savegameId);
- break;
- case GID_JONES:
- // HACK: The code that enables certain menu items isn't called when a game is restored from the
- // launcher, or the "Restore game" option in the game's main menu - bugs #6537 and #6723.
- // These menu entries are disabled when the game is launched, and are enabled when a new game is
- // started. The code for enabling these entries is is all in script 1, room1::init, but that code
- // path is never followed in these two cases (restoring game from the menu, or restoring a game
- // from the ScummVM launcher). Thus, we perform the calls to enable the menus ourselves here.
- // These two are needed when restoring from the launcher
- // FIXME: The original interpreter saves and restores the menu state, so these attributes
- // are automatically reset there. We may want to do the same.
- g_sci->_gfxMenu->kernelSetAttribute(257 >> 8, 257 & 0xFF, SCI_MENU_ATTRIBUTE_ENABLED, TRUE_REG); // Sierra -> About Jones
- g_sci->_gfxMenu->kernelSetAttribute(258 >> 8, 258 & 0xFF, SCI_MENU_ATTRIBUTE_ENABLED, TRUE_REG); // Sierra -> Help
- // The rest are normally enabled from room1::init
- g_sci->_gfxMenu->kernelSetAttribute(769 >> 8, 769 & 0xFF, SCI_MENU_ATTRIBUTE_ENABLED, TRUE_REG); // Options -> Delete current player
- g_sci->_gfxMenu->kernelSetAttribute(513 >> 8, 513 & 0xFF, SCI_MENU_ATTRIBUTE_ENABLED, TRUE_REG); // Game -> Save Game
- g_sci->_gfxMenu->kernelSetAttribute(515 >> 8, 515 & 0xFF, SCI_MENU_ATTRIBUTE_ENABLED, TRUE_REG); // Game -> Restore Game
- g_sci->_gfxMenu->kernelSetAttribute(1025 >> 8, 1025 & 0xFF, SCI_MENU_ATTRIBUTE_ENABLED, TRUE_REG); // Status -> Statistics
- g_sci->_gfxMenu->kernelSetAttribute(1026 >> 8, 1026 & 0xFF, SCI_MENU_ATTRIBUTE_ENABLED, TRUE_REG); // Status -> Goals
- break;
- case GID_PQ2:
- // HACK: Same as above - enable the save game menu option when loading in PQ2 (bug #6875).
- // It gets disabled in the game's death screen.
- g_sci->_gfxMenu->kernelSetAttribute(2, 1, SCI_MENU_ATTRIBUTE_ENABLED, TRUE_REG); // Game -> Save Game
- break;
- default:
- break;
- }
+ gamestate_afterRestoreFixUp(s, savegameId);
+
} else {
s->r_acc = TRUE_REG;
warning("Savegame #%d not found", savegameId);
diff --git a/engines/sci/engine/kgraphics.cpp b/engines/sci/engine/kgraphics.cpp
index 0b945c1eec..73236b98ed 100644
--- a/engines/sci/engine/kgraphics.cpp
+++ b/engines/sci/engine/kgraphics.cpp
@@ -45,6 +45,7 @@
#include "sci/graphics/paint16.h"
#include "sci/graphics/picture.h"
#include "sci/graphics/ports.h"
+#include "sci/graphics/remap.h"
#include "sci/graphics/screen.h"
#include "sci/graphics/text16.h"
#include "sci/graphics/view.h"
@@ -359,12 +360,7 @@ reg_t kTextSize(EngineState *s, int argc, reg_t *argv) {
uint16 languageSplitter = 0;
Common::String splitText = g_sci->strSplitLanguage(text.c_str(), &languageSplitter, sep);
-#ifdef ENABLE_SCI32
- if (g_sci->_gfxText32)
- g_sci->_gfxText32->kernelTextSize(splitText.c_str(), font_nr, maxwidth, &textWidth, &textHeight);
- else
-#endif
- g_sci->_gfxText16->kernelTextSize(splitText.c_str(), languageSplitter, font_nr, maxwidth, &textWidth, &textHeight);
+ g_sci->_gfxText16->kernelTextSize(splitText.c_str(), languageSplitter, font_nr, maxwidth, &textWidth, &textHeight);
// One of the game texts in LB2 German contains loads of spaces in
// its end. We trim the text here, otherwise the graphics code will
@@ -445,8 +441,15 @@ reg_t kCantBeHere(EngineState *s, int argc, reg_t *argv) {
reg_t curObject = argv[0];
reg_t listReference = (argc > 1) ? argv[1] : NULL_REG;
- reg_t canBeHere = g_sci->_gfxCompare->kernelCanBeHere(curObject, listReference);
- return canBeHere;
+#ifdef ENABLE_SCI32
+ if (getSciVersion() >= SCI_VERSION_2) {
+ return g_sci->_gfxCompare->kernelCantBeHere32(curObject, listReference);
+ } else {
+#endif
+ return g_sci->_gfxCompare->kernelCanBeHere(curObject, listReference);
+#ifdef ENABLE_SCI32
+ }
+#endif
}
reg_t kIsItSkip(EngineState *s, int argc, reg_t *argv) {
@@ -492,7 +495,7 @@ reg_t kNumLoops(EngineState *s, int argc, reg_t *argv) {
loopCount = g_sci->_gfxCache->kernelViewGetLoopCount(viewId);
- debugC(kDebugLevelGraphics, "NumLoops(view.%d) = %d", viewId, loopCount);
+ debugC(9, kDebugLevelGraphics, "NumLoops(view.%d) = %d", viewId, loopCount);
return make_reg(0, loopCount);
}
@@ -505,7 +508,7 @@ reg_t kNumCels(EngineState *s, int argc, reg_t *argv) {
celCount = g_sci->_gfxCache->kernelViewGetCelCount(viewId, loopNo);
- debugC(kDebugLevelGraphics, "NumCels(view.%d, %d) = %d", viewId, loopNo, celCount);
+ debugC(9, kDebugLevelGraphics, "NumCels(view.%d, %d) = %d", viewId, loopNo, celCount);
return make_reg(0, celCount);
}
@@ -576,9 +579,17 @@ reg_t kBaseSetter(EngineState *s, int argc, reg_t *argv) {
}
reg_t kSetNowSeen(EngineState *s, int argc, reg_t *argv) {
- g_sci->_gfxCompare->kernelSetNowSeen(argv[0]);
-
- return s->r_acc;
+#ifdef ENABLE_SCI32
+ if (getSciVersion() >= SCI_VERSION_2) {
+ g_sci->_gfxFrameout->kernelSetNowSeen(argv[0]);
+ return NULL_REG;
+ } else {
+#endif
+ g_sci->_gfxCompare->kernelSetNowSeen(argv[0]);
+ return s->r_acc;
+#ifdef ENABLE_SCI32
+ }
+#endif
}
reg_t kPalette(EngineState *s, int argc, reg_t *argv) {
@@ -1247,22 +1258,22 @@ reg_t kShow(EngineState *s, int argc, reg_t *argv) {
}
// Early variant of the SCI32 kRemapColors kernel function, used in the demo of QFG4
-reg_t kRemapColors(EngineState *s, int argc, reg_t *argv) {
+reg_t kRemapColors16(EngineState *s, int argc, reg_t *argv) {
uint16 operation = argv[0].toUint16();
switch (operation) {
case 0: { // remap by percent
uint16 percent = argv[1].toUint16();
- g_sci->_gfxPalette16->resetRemapping();
- g_sci->_gfxPalette16->setRemappingPercent(254, percent);
+ g_sci->_gfxRemap16->resetRemapping();
+ g_sci->_gfxRemap16->setRemappingPercent(254, percent);
}
break;
case 1: { // remap by range
uint16 from = argv[1].toUint16();
uint16 to = argv[2].toUint16();
uint16 base = argv[3].toUint16();
- g_sci->_gfxPalette16->resetRemapping();
- g_sci->_gfxPalette16->setRemappingRange(254, from, to, base);
+ g_sci->_gfxRemap16->resetRemapping();
+ g_sci->_gfxRemap16->setRemappingRange(254, from, to, base);
}
break;
case 2: // turn remapping off (unused)
diff --git a/engines/sci/engine/kgraphics32.cpp b/engines/sci/engine/kgraphics32.cpp
index 8d41393a9e..7850a10006 100644
--- a/engines/sci/engine/kgraphics32.cpp
+++ b/engines/sci/engine/kgraphics32.cpp
@@ -45,15 +45,17 @@
#include "sci/graphics/paint16.h"
#include "sci/graphics/picture.h"
#include "sci/graphics/ports.h"
+#include "sci/graphics/remap.h"
#include "sci/graphics/screen.h"
#include "sci/graphics/text16.h"
#include "sci/graphics/view.h"
#ifdef ENABLE_SCI32
-#include "sci/graphics/palette32.h"
+#include "sci/graphics/celobj32.h"
#include "sci/graphics/controls32.h"
#include "sci/graphics/font.h" // TODO: remove once kBitmap is moved in a separate class
-#include "sci/graphics/text32.h"
#include "sci/graphics/frameout.h"
+#include "sci/graphics/palette32.h"
+#include "sci/graphics/text32.h"
#endif
namespace Sci {
@@ -62,63 +64,67 @@ namespace Sci {
extern void showScummVMDialog(const Common::String &message);
reg_t kIsHiRes(EngineState *s, int argc, reg_t *argv) {
- // Returns 0 if the screen width or height is less than 640 or 400,
- // respectively.
- if (g_system->getWidth() < 640 || g_system->getHeight() < 400)
+ const Buffer &buffer = g_sci->_gfxFrameout->getCurrentBuffer();
+ if (buffer.screenWidth < 640 || buffer.screenHeight < 400)
return make_reg(0, 0);
return make_reg(0, 1);
}
-// SCI32 variant, can't work like sci16 variants
-reg_t kCantBeHere32(EngineState *s, int argc, reg_t *argv) {
- // TODO
-// reg_t curObject = argv[0];
-// reg_t listReference = (argc > 1) ? argv[1] : NULL_REG;
-
- return NULL_REG;
-}
-
reg_t kAddScreenItem(EngineState *s, int argc, reg_t *argv) {
- if (g_sci->_gfxFrameout->findScreenItem(argv[0]) == NULL)
- g_sci->_gfxFrameout->kernelAddScreenItem(argv[0]);
- else
- g_sci->_gfxFrameout->kernelUpdateScreenItem(argv[0]);
+ debugC(6, kDebugLevelGraphics, "kAddScreenItem %x:%x (%s)", PRINT_REG(argv[0]), s->_segMan->getObjectName(argv[0]));
+ g_sci->_gfxFrameout->kernelAddScreenItem(argv[0]);
return s->r_acc;
}
reg_t kUpdateScreenItem(EngineState *s, int argc, reg_t *argv) {
+ debugC(7, kDebugLevelGraphics, "kUpdateScreenItem %x:%x (%s)", PRINT_REG(argv[0]), s->_segMan->getObjectName(argv[0]));
g_sci->_gfxFrameout->kernelUpdateScreenItem(argv[0]);
return s->r_acc;
}
reg_t kDeleteScreenItem(EngineState *s, int argc, reg_t *argv) {
+ debugC(6, kDebugLevelGraphics, "kDeleteScreenItem %x:%x (%s)", PRINT_REG(argv[0]), s->_segMan->getObjectName(argv[0]));
g_sci->_gfxFrameout->kernelDeleteScreenItem(argv[0]);
return s->r_acc;
}
reg_t kAddPlane(EngineState *s, int argc, reg_t *argv) {
+ debugC(6, kDebugLevelGraphics, "kAddPlane %x:%x (%s)", PRINT_REG(argv[0]), s->_segMan->getObjectName(argv[0]));
g_sci->_gfxFrameout->kernelAddPlane(argv[0]);
return s->r_acc;
}
+reg_t kUpdatePlane(EngineState *s, int argc, reg_t *argv) {
+ debugC(7, kDebugLevelGraphics, "kUpdatePlane %x:%x (%s)", PRINT_REG(argv[0]), s->_segMan->getObjectName(argv[0]));
+ g_sci->_gfxFrameout->kernelUpdatePlane(argv[0]);
+ return s->r_acc;
+}
+
reg_t kDeletePlane(EngineState *s, int argc, reg_t *argv) {
+ debugC(6, kDebugLevelGraphics, "kDeletePlane %x:%x (%s)", PRINT_REG(argv[0]), s->_segMan->getObjectName(argv[0]));
g_sci->_gfxFrameout->kernelDeletePlane(argv[0]);
return s->r_acc;
}
-reg_t kUpdatePlane(EngineState *s, int argc, reg_t *argv) {
- g_sci->_gfxFrameout->kernelUpdatePlane(argv[0]);
+reg_t kMovePlaneItems(EngineState *s, int argc, reg_t *argv) {
+ const reg_t plane = argv[0];
+ const int16 deltaX = argv[1].toSint16();
+ const int16 deltaY = argv[2].toSint16();
+ const bool scrollPics = argc > 3 ? argv[3].toUint16() : false;
+
+ g_sci->_gfxFrameout->kernelMovePlaneItems(plane, deltaX, deltaY, scrollPics);
return s->r_acc;
}
reg_t kAddPicAt(EngineState *s, int argc, reg_t *argv) {
reg_t planeObj = argv[0];
GuiResourceId pictureId = argv[1].toUint16();
- int16 pictureX = argv[2].toSint16();
- int16 pictureY = argv[3].toSint16();
+ int16 x = argv[2].toSint16();
+ int16 y = argv[3].toSint16();
+ bool mirrorX = argc > 4 ? argv[4].toSint16() : false;
- g_sci->_gfxFrameout->kernelAddPicAt(planeObj, pictureId, pictureX, pictureY);
+ g_sci->_gfxFrameout->kernelAddPicAt(planeObj, pictureId, x, y, mirrorX);
return s->r_acc;
}
@@ -127,44 +133,16 @@ reg_t kGetHighPlanePri(EngineState *s, int argc, reg_t *argv) {
}
reg_t kFrameOut(EngineState *s, int argc, reg_t *argv) {
-/* TODO: Transcribed from SCI engine disassembly.
- GraphicsMgr &graphicsMgr = g_sci->_graphicsMgr;
- if (graphicsMgr.palMorphNeeded) {
- graphicsMgr.PalMorphFrameOut(&g_PalStyleRanges, false);
- }
- else {
- // TODO: Not sure if this is a pointer or not yet.
- if (g_ScrollState != nullptr) {
- kFrameOutDoScroll();
- }
-
- bool showBits = true;
- if (argc == 1) {
- showBits = (bool) argv[0].toUint16();
- }
-
- rect SOL_Rect = { .left = 0, .top = 0, .right = UINT32_MAX, .bottom = UINT32_MAX };
- graphicsMgr.FrameOut(showBits, &rect);
- }
-*/
- g_sci->_gfxFrameout->kernelFrameout();
- return NULL_REG;
+ bool showBits = argc > 0 ? argv[0].toUint16() : true;
+ g_sci->_gfxFrameout->kernelFrameOut(showBits);
+ s->speedThrottler(16);
+ s->_throttleTrigger = true;
+ return s->r_acc;
}
reg_t kSetPalStyleRange(EngineState *s, int argc, reg_t *argv) {
-/* TODO: Transcribed from SCI engine disassembly.
- uint16 start = argv[0].toUint16();
- uint16 end = argv[1].toUint16();
- if (end <= start) {
- uint16 index = start;
- while (index <= end) {
- g_PalStyleRanges[index] = 0;
- }
- }
-*/
-
- kStub(s, argc, argv);
- return NULL_REG;
+ g_sci->_gfxFrameout->kernelSetPalStyleRange(argv[0].toUint16(), argv[1].toUint16());
+ return s->r_acc;
}
reg_t kObjectIntersect(EngineState *s, int argc, reg_t *argv) {
@@ -173,81 +151,92 @@ reg_t kObjectIntersect(EngineState *s, int argc, reg_t *argv) {
return make_reg(0, objRect1.intersects(objRect2));
}
-// Tests if the coordinate is on the passed object
reg_t kIsOnMe(EngineState *s, int argc, reg_t *argv) {
- uint16 x = argv[0].toUint16();
- uint16 y = argv[1].toUint16();
- reg_t targetObject = argv[2];
- uint16 illegalBits = argv[3].getOffset();
- Common::Rect nsRect = g_sci->_gfxCompare->getNSRect(targetObject);
-
- uint16 itemX = readSelectorValue(s->_segMan, targetObject, SELECTOR(x));
- uint16 itemY = readSelectorValue(s->_segMan, targetObject, SELECTOR(y));
- // If top and left are negative, we need to adjust coordinates by the item's x and y
- if (nsRect.left < 0)
- nsRect.translate(itemX, 0);
- if (nsRect.top < 0)
- nsRect.translate(0, itemY);
-
- // we assume that x, y are local coordinates
-
- bool contained = nsRect.contains(x, y);
- if (contained && illegalBits) {
- // If illegalbits are set, we check the color of the pixel that got clicked on
- // for now, we return false if the pixel is transparent
- // although illegalBits may get differently set, don't know yet how this really works out
- uint16 viewId = readSelectorValue(s->_segMan, targetObject, SELECTOR(view));
- int16 loopNo = readSelectorValue(s->_segMan, targetObject, SELECTOR(loop));
- int16 celNo = readSelectorValue(s->_segMan, targetObject, SELECTOR(cel));
- if (g_sci->_gfxCompare->kernelIsItSkip(viewId, loopNo, celNo, Common::Point(x - nsRect.left, y - nsRect.top)))
- contained = false;
- }
- return make_reg(0, contained);
+ int16 x = argv[0].toSint16();
+ int16 y = argv[1].toSint16();
+ reg_t object = argv[2];
+ bool checkPixel = argv[3].toSint16();
+
+ return g_sci->_gfxFrameout->kernelIsOnMe(object, Common::Point(x, y), checkPixel);
}
reg_t kCreateTextBitmap(EngineState *s, int argc, reg_t *argv) {
- switch (argv[0].toUint16()) {
- case 0: {
- if (argc != 4) {
- warning("kCreateTextBitmap(0): expected 4 arguments, got %i", argc);
- return NULL_REG;
- }
- reg_t object = argv[3];
- Common::String text = s->_segMan->getString(readSelector(s->_segMan, object, SELECTOR(text)));
- debugC(kDebugLevelStrings, "kCreateTextBitmap case 0 (%04x:%04x, %04x:%04x, %04x:%04x)",
- PRINT_REG(argv[1]), PRINT_REG(argv[2]), PRINT_REG(argv[3]));
- debugC(kDebugLevelStrings, "%s", text.c_str());
- int16 maxWidth = argv[1].toUint16();
- int16 maxHeight = argv[2].toUint16();
- g_sci->_gfxCoordAdjuster->fromScriptToDisplay(maxHeight, maxWidth);
- // These values can be larger than the screen in the SQ6 demo, room 100
- // TODO: Find out why. For now, don't show any text in that room.
- if (g_sci->getGameId() == GID_SQ6 && g_sci->isDemo() && s->currentRoomNumber() == 100)
- return NULL_REG;
- return g_sci->_gfxText32->createTextBitmap(object, maxWidth, maxHeight);
- }
- case 1: {
- if (argc != 2) {
- warning("kCreateTextBitmap(1): expected 2 arguments, got %i", argc);
- return NULL_REG;
- }
- reg_t object = argv[1];
- Common::String text = s->_segMan->getString(readSelector(s->_segMan, object, SELECTOR(text)));
- debugC(kDebugLevelStrings, "kCreateTextBitmap case 1 (%04x:%04x)", PRINT_REG(argv[1]));
- debugC(kDebugLevelStrings, "%s", text.c_str());
- return g_sci->_gfxText32->createTextBitmap(object);
- }
- default:
- warning("CreateTextBitmap(%d)", argv[0].toUint16());
+ SegManager *segMan = s->_segMan;
+
+ int16 subop = argv[0].toUint16();
+
+ int16 width = 0;
+ int16 height = 0;
+ reg_t object;
+
+ if (subop == 0) {
+ width = argv[1].toUint16();
+ height = argv[2].toUint16();
+ object = argv[3];
+ } else if (subop == 1) {
+ object = argv[1];
+ } else {
+ warning("Invalid kCreateTextBitmap subop %d", subop);
return NULL_REG;
}
+
+ Common::String text = segMan->getString(readSelector(segMan, object, SELECTOR(text)));
+ int16 foreColor = readSelectorValue(segMan, object, SELECTOR(fore));
+ int16 backColor = readSelectorValue(segMan, object, SELECTOR(back));
+ int16 skipColor = readSelectorValue(segMan, object, SELECTOR(skip));
+ GuiResourceId fontId = (GuiResourceId)readSelectorValue(segMan, object, SELECTOR(font));
+ int16 borderColor = readSelectorValue(segMan, object, SELECTOR(borderColor));
+ int16 dimmed = readSelectorValue(segMan, object, SELECTOR(dimmed));
+
+ Common::Rect rect(
+ readSelectorValue(segMan, object, SELECTOR(textLeft)),
+ readSelectorValue(segMan, object, SELECTOR(textTop)),
+ readSelectorValue(segMan, object, SELECTOR(textRight)) + 1,
+ readSelectorValue(segMan, object, SELECTOR(textBottom)) + 1
+ );
+
+ if (subop == 0) {
+ TextAlign alignment = (TextAlign)readSelectorValue(segMan, object, SELECTOR(mode));
+ return g_sci->_gfxText32->createFontBitmap(width, height, rect, text, foreColor, backColor, skipColor, fontId, alignment, borderColor, dimmed, true);
+ } else {
+ CelInfo32 celInfo;
+ celInfo.type = kCelTypeView;
+ celInfo.resourceId = readSelectorValue(segMan, object, SELECTOR(view));
+ celInfo.loopNo = readSelectorValue(segMan, object, SELECTOR(loop));
+ celInfo.celNo = readSelectorValue(segMan, object, SELECTOR(cel));
+ return g_sci->_gfxText32->createFontBitmap(celInfo, rect, text, foreColor, backColor, fontId, skipColor, borderColor, dimmed);
+ }
+}
+
+reg_t kText(EngineState *s, int argc, reg_t *argv) {
+ if (!s)
+ return make_reg(0, getSciVersion());
+ error("not supposed to call this");
}
-reg_t kDisposeTextBitmap(EngineState *s, int argc, reg_t *argv) {
- g_sci->_gfxText32->disposeTextBitmap(argv[0]);
+reg_t kTextSize32(EngineState *s, int argc, reg_t *argv) {
+ g_sci->_gfxText32->setFont(argv[2].toUint16());
+
+ reg_t *rect = s->_segMan->derefRegPtr(argv[0], 4);
+
+ Common::String text = s->_segMan->getString(argv[1]);
+ int16 maxWidth = argc > 3 ? argv[3].toSint16() : 0;
+ bool doScaling = argc > 4 ? argv[4].toSint16() : true;
+
+ Common::Rect textRect = g_sci->_gfxText32->getTextSize(text, maxWidth, doScaling);
+ rect[0] = make_reg(0, textRect.left);
+ rect[1] = make_reg(0, textRect.top);
+ rect[2] = make_reg(0, textRect.right - 1);
+ rect[3] = make_reg(0, textRect.bottom - 1);
return s->r_acc;
}
+reg_t kTextWidth(EngineState *s, int argc, reg_t *argv) {
+ g_sci->_gfxText32->setFont(argv[1].toUint16());
+ Common::String text = s->_segMan->getString(argv[0]);
+ return make_reg(0, g_sci->_gfxText32->getStringWidth(text));
+}
+
reg_t kWinHelp(EngineState *s, int argc, reg_t *argv) {
switch (argv[0].toUint16()) {
case 1:
@@ -266,98 +255,137 @@ reg_t kWinHelp(EngineState *s, int argc, reg_t *argv) {
}
/**
- * Used for scene transitions, replacing (but reusing parts of) the old
- * transition code.
+ * Causes an immediate plane transition with an optional transition
+ * effect
*/
reg_t kSetShowStyle(EngineState *s, int argc, reg_t *argv) {
- // Can be called with 7 or 8 parameters
- // The style defines which transition to perform. Related to the transition
- // tables inside graphics/transitions.cpp
- uint16 showStyle = argv[0].toUint16(); // 0 - 15
- reg_t planeObj = argv[1]; // the affected plane
- Common::String planeObjName = s->_segMan->getObjectName(planeObj);
- uint16 seconds = argv[2].toUint16(); // seconds that the transition lasts
- uint16 backColor = argv[3].toUint16(); // target back color(?). When fading out, it's 0x0000. When fading in, it's 0xffff
- int16 priority = argv[4].toSint16(); // always 0xc8 (200) when fading in/out
- uint16 animate = argv[5].toUint16(); // boolean, animate or not while the transition lasts
- uint16 refFrame = argv[6].toUint16(); // refFrame, always 0 when fading in/out
+ ShowStyleType type = (ShowStyleType)argv[0].toUint16();
+ reg_t planeObj = argv[1];
+ int16 seconds = argv[2].toSint16();
+ // NOTE: This value seems to indicate whether the transition is an
+ // “exit” transition (0) or an “enter” transition (-1) for fade
+ // transitions. For other types of transitions, it indicates a palette
+ // index value to use when filling the screen.
+ int16 back = argv[3].toSint16();
+ int16 priority = argv[4].toSint16();
+ int16 animate = argv[5].toSint16();
+ // TODO: Rename to frameOutNow?
+ int16 refFrame = argv[6].toSint16();
+ int16 blackScreen;
+ reg_t pFadeArray;
int16 divisions;
- // If the game has the pFadeArray selector, another parameter is used here,
- // before the optional last parameter
- bool hasFadeArray = g_sci->getKernel()->findSelector("pFadeArray") > 0;
- if (hasFadeArray) {
- // argv[7]
- divisions = (argc >= 9) ? argv[8].toSint16() : -1; // divisions (transition steps?)
- } else {
- divisions = (argc >= 8) ? argv[7].toSint16() : -1; // divisions (transition steps?)
+ // SCI 2–2.1early
+ if (getSciVersion() < SCI_VERSION_2_1_MIDDLE) {
+ blackScreen = 0;
+ pFadeArray = NULL_REG;
+ divisions = argc > 7 ? argv[7].toSint16() : -1;
}
-
- if (showStyle > 15) {
- warning("kSetShowStyle: Illegal style %d for plane %04x:%04x", showStyle, PRINT_REG(planeObj));
- return s->r_acc;
+ // SCI 2.1mid–2.1late
+ else if (getSciVersion() < SCI_VERSION_3) {
+ blackScreen = 0;
+ pFadeArray = argc > 7 ? argv[7] : NULL_REG;
+ divisions = argc > 8 ? argv[8].toSint16() : -1;
+ }
+ // SCI 3
+ else {
+ blackScreen = argv[7].toSint16();
+ pFadeArray = argc > 8 ? argv[8] : NULL_REG;
+ divisions = argc > 9 ? argv[9].toSint16() : -1;
}
- // GK1 calls fadeout (13) / fadein (14) with the following parameters:
- // seconds: 1
- // backColor: 0 / -1
- // fade: 200
- // animate: 0
- // refFrame: 0
- // divisions: 0 / 20
+// TODO: Reuse later for SCI2 and SCI3 implementation and then discard
+// warning("kSetShowStyle: effect %d, plane: %04x:%04x (%s), sec: %d, "
+// "dir: %d, prio: %d, animate: %d, ref frame: %d, black screen: %d, "
+// "pFadeArray: %04x:%04x (%s), divisions: %d",
+// type, PRINT_REG(planeObj), s->_segMan->getObjectName(planeObj), seconds,
+// back, priority, animate, refFrame, blackScreen,
+// PRINT_REG(pFadeArray), s->_segMan->getObjectName(pFadeArray), divisions);
- // TODO: Check if the plane is in the list of planes to draw
+ // NOTE: The order of planeObj and showStyle are reversed
+ // because this is how SCI3 called the corresponding method
+ // on the KernelMgr
+ g_sci->_gfxFrameout->kernelSetShowStyle(argc, planeObj, type, seconds, back, priority, animate, refFrame, pFadeArray, divisions, blackScreen);
- Common::String effectName = "unknown";
+ return s->r_acc;
+}
- switch (showStyle) {
- case 0: // no transition / show
- effectName = "show";
+reg_t kCelHigh32(EngineState *s, int argc, reg_t *argv) {
+ GuiResourceId resourceId = argv[0].toUint16();
+ int16 loopNo = argv[1].toSint16();
+ int16 celNo = argv[2].toSint16();
+ CelObjView celObj(resourceId, loopNo, celNo);
+ return make_reg(0, mulru(celObj._height, Ratio(g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight, celObj._scaledHeight)));
+}
+
+reg_t kCelWide32(EngineState *s, int argc, reg_t *argv) {
+ GuiResourceId resourceId = argv[0].toUint16();
+ int16 loopNo = argv[1].toSint16();
+ int16 celNo = argv[2].toSint16();
+ CelObjView celObj(resourceId, loopNo, celNo);
+ return make_reg(0, mulru(celObj._width, Ratio(g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth, celObj._scaledWidth)));
+}
+
+reg_t kCelInfo(EngineState *s, int argc, reg_t *argv) {
+ // Used by Shivers 1, room 23601 to determine what blocks on the red door puzzle board
+ // are occupied by pieces already
+
+ CelObjView view(argv[1].toUint16(), argv[2].toSint16(), argv[3].toSint16());
+
+ int16 result = 0;
+
+ switch (argv[0].toUint16()) {
+ case 0:
+ result = view._displace.x;
break;
- case 13: // fade out
- effectName = "fade out";
- // TODO
+ case 1:
+ result = view._displace.y;
break;
- case 14: // fade in
- effectName = "fade in";
- // TODO
+ case 2:
+ case 3:
+ // null operation
break;
- default:
- // TODO
+ case 4:
+ result = view.readPixel(argv[4].toSint16(), argv[5].toSint16(), view._mirrorX);
break;
}
- warning("kSetShowStyle: effect %d (%s) - plane: %04x:%04x (%s), sec: %d, "
- "back: %d, prio: %d, animate: %d, ref frame: %d, divisions: %d",
- showStyle, effectName.c_str(), PRINT_REG(planeObj), planeObjName.c_str(),
- seconds, backColor, priority, animate, refFrame, divisions);
- return s->r_acc;
+ return make_reg(0, result);
}
-reg_t kCelInfo(EngineState *s, int argc, reg_t *argv) {
- // Used by Shivers 1, room 23601 to determine what blocks on the red door puzzle board
- // are occupied by pieces already
+reg_t kScrollWindow(EngineState *s, int argc, reg_t *argv) {
+ if (!s)
+ return make_reg(0, getSciVersion());
+ error("not supposed to call this");
+}
- switch (argv[0].toUint16()) { // subops 0 - 4
- // 0 - return the view
- // 1 - return the loop
- // 2, 3 - nop
- case 4: {
- GuiResourceId viewId = argv[1].toSint16();
- int16 loopNo = argv[2].toSint16();
- int16 celNo = argv[3].toSint16();
- int16 x = argv[4].toUint16();
- int16 y = argv[5].toUint16();
- byte color = g_sci->_gfxCache->kernelViewGetColorAtCoordinate(viewId, loopNo, celNo, x, y);
- return make_reg(0, color);
- }
- default: {
- kStub(s, argc, argv);
- return s->r_acc;
- }
- }
+reg_t kScrollWindowCreate(EngineState *s, int argc, reg_t *argv) {
+ debug("kScrollWindowCreate");
+ kStub(s, argc, argv);
+ return argv[0];
+}
+
+reg_t kScrollWindowAdd(EngineState *s, int argc, reg_t *argv) {
+ debug("kScrollWindowAdd");
+ return kStubNull(s, argc, argv);
}
+reg_t kScrollWindowWhere(EngineState *s, int argc, reg_t *argv) {
+ debug("kScrollWindowWhere");
+ return kStubNull(s, argc, argv);
+}
+
+reg_t kScrollWindowShow(EngineState *s, int argc, reg_t *argv) {
+ debug("kScrollWindowShow");
+ return kStubNull(s, argc, argv);
+}
+
+reg_t kScrollWindowDestroy(EngineState *s, int argc, reg_t *argv) {
+ debug("kScrollWindowDestroy");
+ return kStubNull(s, argc, argv);
+}
+
+#if 0
reg_t kScrollWindow(EngineState *s, int argc, reg_t *argv) {
// Used by SQ6 and LSL6 hires for the text area in the bottom of the
// screen. The relevant scripts also exist in Phantasmagoria 1, but they're
@@ -465,226 +493,233 @@ reg_t kScrollWindow(EngineState *s, int argc, reg_t *argv) {
return s->r_acc;
}
+#endif
+
+reg_t kFont(EngineState *s, int argc, reg_t *argv) {
+ if (!s)
+ return make_reg(0, getSciVersion());
+ error("not supposed to call this");
+}
+
+reg_t kSetFontHeight(EngineState *s, int argc, reg_t *argv) {
+ // TODO: Setting font may have just been for side effect
+ // of setting the fontHeight on the font manager, in
+ // which case we could just get the font directly ourselves.
+ g_sci->_gfxText32->setFont(argv[0].toUint16());
+ g_sci->_gfxText32->_scaledHeight = (g_sci->_gfxText32->_font->getHeight() * g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight + g_sci->_gfxText32->_scaledHeight - 1) / g_sci->_gfxText32->_scaledHeight;
+ return make_reg(0, g_sci->_gfxText32->_scaledHeight);
+}
reg_t kSetFontRes(EngineState *s, int argc, reg_t *argv) {
- // TODO: This defines the resolution that the fonts are supposed to be displayed
- // in. Currently, this is only used for showing high-res fonts in GK1 Mac, but
- // should be extended to handle other font resolutions such as those
+ g_sci->_gfxText32->_scaledWidth = argv[0].toUint16();
+ g_sci->_gfxText32->_scaledHeight = argv[1].toUint16();
+ return s->r_acc;
+}
- int xResolution = argv[0].toUint16();
- //int yResolution = argv[1].toUint16();
+reg_t kBitmap(EngineState *s, int argc, reg_t *argv) {
+ if (!s)
+ return make_reg(0, getSciVersion());
+ error("not supposed to call this");
+}
- g_sci->_gfxScreen->setFontIsUpscaled(xResolution == 640 &&
- g_sci->_gfxScreen->getUpscaledHires() != GFX_SCREEN_UPSCALED_DISABLED);
+reg_t kBitmapCreate(EngineState *s, int argc, reg_t *argv) {
+ int16 width = argv[0].toSint16();
+ int16 height = argv[1].toSint16();
+ int16 skipColor = argv[2].toSint16();
+ int16 backColor = argv[3].toSint16();
+ int16 scaledWidth = argc > 4 ? argv[4].toSint16() : g_sci->_gfxText32->_scaledWidth;
+ int16 scaledHeight = argc > 5 ? argv[5].toSint16() : g_sci->_gfxText32->_scaledHeight;
+ bool useRemap = argc > 6 ? argv[6].toSint16() : false;
- return s->r_acc;
+ BitmapResource bitmap(s->_segMan, width, height, skipColor, 0, 0, scaledWidth, scaledHeight, 0, useRemap);
+ memset(bitmap.getPixels(), backColor, width * height);
+ return bitmap.getObject();
}
-reg_t kFont(EngineState *s, int argc, reg_t *argv) {
- // Handle font settings for SCI2.1
+reg_t kBitmapDestroy(EngineState *s, int argc, reg_t *argv) {
+ s->_segMan->freeHunkEntry(argv[0]);
+ return s->r_acc;
+}
- switch (argv[0].toUint16()) {
- case 1:
- // Set font resolution
- return kSetFontRes(s, argc - 1, argv + 1);
- default:
- warning("kFont: unknown subop %d", argv[0].toUint16());
+reg_t kBitmapDrawLine(EngineState *s, int argc, reg_t *argv) {
+ // bitmapMemId, (x1, y1, x2, y2) OR (x2, y2, x1, y1), line color, unknown int, unknown int
+ return kStubNull(s, argc + 1, argv - 1);
+}
+
+reg_t kBitmapDrawView(EngineState *s, int argc, reg_t *argv) {
+ // viewId, loopNo, celNo, displace x, displace y, unused, view x, view y
+
+ // called e.g. from TiledBitmap::resize() in Torin's Passage, script 64869
+ // The tiled view seems to always have 2 loops.
+ // These loops need to have 1 cel in loop 0 and 8 cels in loop 1.
+
+ return kStubNull(s, argc + 1, argv - 1);
+
+#if 0
+ // tiled surface
+ // 6 params, called e.g. from TiledBitmap::resize() in Torin's Passage,
+ // script 64869
+ reg_t hunkId = argv[1]; // obtained from kBitmap(0)
+ // The tiled view seems to always have 2 loops.
+ // These loops need to have 1 cel in loop 0 and 8 cels in loop 1.
+ uint16 viewNum = argv[2].toUint16(); // vTiles selector
+ uint16 loop = argv[3].toUint16();
+ uint16 cel = argv[4].toUint16();
+ uint16 x = argv[5].toUint16();
+ uint16 y = argv[6].toUint16();
+
+ byte *memoryPtr = s->_segMan->getHunkPointer(hunkId);
+ // Get totalWidth, totalHeight
+ uint16 totalWidth = READ_LE_UINT16(memoryPtr);
+ uint16 totalHeight = READ_LE_UINT16(memoryPtr + 2);
+ byte *bitmap = memoryPtr + BITMAP_HEADER_SIZE;
+
+ GfxView *view = g_sci->_gfxCache->getView(viewNum);
+ uint16 tileWidth = view->getWidth(loop, cel);
+ uint16 tileHeight = view->getHeight(loop, cel);
+ const byte *tileBitmap = view->getBitmap(loop, cel);
+ uint16 width = MIN<uint16>(totalWidth - x, tileWidth);
+ uint16 height = MIN<uint16>(totalHeight - y, tileHeight);
+
+ for (uint16 curY = 0; curY < height; curY++) {
+ for (uint16 curX = 0; curX < width; curX++) {
+ bitmap[(curY + y) * totalWidth + (curX + x)] = tileBitmap[curY * tileWidth + curX];
+ }
}
+#endif
+}
+
+reg_t kBitmapDrawText(EngineState *s, int argc, reg_t *argv) {
+ // called e.g. from TextButton::createBitmap() in Torin's Passage, script 64894
+
+ BitmapResource bitmap(argv[0]);
+ Common::String text = s->_segMan->getString(argv[1]);
+ Common::Rect textRect(
+ argv[2].toSint16(),
+ argv[3].toSint16(),
+ argv[4].toSint16() + 1,
+ argv[5].toSint16() + 1
+ );
+ int16 foreColor = argv[6].toSint16();
+ int16 backColor = argv[7].toSint16();
+ int16 skipColor = argv[8].toSint16();
+ GuiResourceId fontId = (GuiResourceId)argv[9].toUint16();
+ TextAlign alignment = (TextAlign)argv[10].toSint16();
+ int16 borderColor = argv[11].toSint16();
+ bool dimmed = argv[12].toUint16();
+
+ // NOTE: Technically the engine checks these things:
+ // textRect.bottom > 0
+ // textRect.right > 0
+ // textRect.left < bitmap.width
+ // textRect.top < bitmap.height
+ // Then clips. But this seems stupid.
+ textRect.clip(Common::Rect(bitmap.getWidth(), bitmap.getHeight()));
+
+ reg_t textBitmapObject = g_sci->_gfxText32->createFontBitmap(textRect.width(), textRect.height(), Common::Rect(textRect.width(), textRect.height()), text, foreColor, backColor, skipColor, fontId, alignment, borderColor, dimmed, false);
+ Buffer bitmapBuffer(bitmap.getWidth(), bitmap.getHeight(), bitmap.getPixels());
+ CelObjMem textCel(textBitmapObject);
+ textCel.draw(bitmapBuffer, textRect, Common::Point(textRect.left, textRect.top), false);
+ s->_segMan->freeHunkEntry(textBitmapObject);
return s->r_acc;
}
-// TODO: Eventually, all of the kBitmap operations should be put
-// in a separate class
+reg_t kBitmapDrawColor(EngineState *s, int argc, reg_t *argv) {
+ // called e.g. from TextView::init() and TextView::draw() in Torin's Passage, script 64890
-#define BITMAP_HEADER_SIZE 46
+ BitmapResource bitmap(argv[0]);
+ Common::Rect fillRect(
+ argv[1].toSint16(),
+ argv[2].toSint16(),
+ argv[3].toSint16() + 1,
+ argv[4].toSint16() + 1
+ );
-reg_t kBitmap(EngineState *s, int argc, reg_t *argv) {
- // Used for bitmap operations in SCI2.1 and SCI3.
- // This is the SCI2.1 version, the functionality seems to have changed in SCI3.
+ Buffer buffer(bitmap.getWidth(), bitmap.getHeight(), bitmap.getPixels());
+ buffer.fillRect(fillRect, argv[5].toSint16());
+ return s->r_acc;
+}
- switch (argv[0].toUint16()) {
- case 0: // init bitmap surface
- {
- // 6 params, called e.g. from TextView::init() in Torin's Passage,
- // script 64890 and TransView::init() in script 64884
- uint16 width = argv[1].toUint16();
- uint16 height = argv[2].toUint16();
- //uint16 skip = argv[3].toUint16();
- uint16 back = argv[4].toUint16(); // usually equals skip
- //uint16 width2 = (argc >= 6) ? argv[5].toUint16() : 0;
- //uint16 height2 = (argc >= 7) ? argv[6].toUint16() : 0;
- //uint16 transparentFlag = (argc >= 8) ? argv[7].toUint16() : 0;
-
- // TODO: skip, width2, height2, transparentFlag
- // (used for transparent bitmaps)
- int entrySize = width * height + BITMAP_HEADER_SIZE;
- reg_t memoryId = s->_segMan->allocateHunkEntry("Bitmap()", entrySize);
- byte *memoryPtr = s->_segMan->getHunkPointer(memoryId);
- memset(memoryPtr, 0, BITMAP_HEADER_SIZE); // zero out the bitmap header
- memset(memoryPtr + BITMAP_HEADER_SIZE, back, width * height);
- // Save totalWidth, totalHeight
- // TODO: Save the whole bitmap header, like SSCI does
- WRITE_LE_UINT16(memoryPtr, width);
- WRITE_LE_UINT16(memoryPtr + 2, height);
- return memoryId;
- }
- break;
- case 1: // dispose text bitmap surface
- return kDisposeTextBitmap(s, argc - 1, argv + 1);
- case 2: // dispose bitmap surface, with extra param
- // 2 params, called e.g. from MenuItem::dispose in Torin's Passage,
- // script 64893
- warning("kBitmap(2), unk1 %d, bitmap ptr %04x:%04x", argv[1].toUint16(), PRINT_REG(argv[2]));
- break;
- case 3: // tiled surface
- {
- // 6 params, called e.g. from TiledBitmap::resize() in Torin's Passage,
- // script 64869
- reg_t hunkId = argv[1]; // obtained from kBitmap(0)
- // The tiled view seems to always have 2 loops.
- // These loops need to have 1 cel in loop 0 and 8 cels in loop 1.
- uint16 viewNum = argv[2].toUint16(); // vTiles selector
- uint16 loop = argv[3].toUint16();
- uint16 cel = argv[4].toUint16();
- uint16 x = argv[5].toUint16();
- uint16 y = argv[6].toUint16();
-
- byte *memoryPtr = s->_segMan->getHunkPointer(hunkId);
- // Get totalWidth, totalHeight
- uint16 totalWidth = READ_LE_UINT16(memoryPtr);
- uint16 totalHeight = READ_LE_UINT16(memoryPtr + 2);
- byte *bitmap = memoryPtr + BITMAP_HEADER_SIZE;
-
- GfxView *view = g_sci->_gfxCache->getView(viewNum);
- uint16 tileWidth = view->getWidth(loop, cel);
- uint16 tileHeight = view->getHeight(loop, cel);
- const byte *tileBitmap = view->getBitmap(loop, cel);
- uint16 width = MIN<uint16>(totalWidth - x, tileWidth);
- uint16 height = MIN<uint16>(totalHeight - y, tileHeight);
-
- for (uint16 curY = 0; curY < height; curY++) {
- for (uint16 curX = 0; curX < width; curX++) {
- bitmap[(curY + y) * totalWidth + (curX + x)] = tileBitmap[curY * tileWidth + curX];
- }
- }
+reg_t kBitmapDrawBitmap(EngineState *s, int argc, reg_t *argv) {
+ // target bitmap, source bitmap, x, y, unknown boolean
- }
- break;
- case 4: // add text to bitmap
- {
- // 13 params, called e.g. from TextButton::createBitmap() in Torin's Passage,
- // script 64894
- reg_t hunkId = argv[1]; // obtained from kBitmap(0)
- Common::String text = s->_segMan->getString(argv[2]);
- uint16 textX = argv[3].toUint16();
- uint16 textY = argv[4].toUint16();
- //reg_t unk5 = argv[5];
- //reg_t unk6 = argv[6];
- //reg_t unk7 = argv[7]; // skip?
- //reg_t unk8 = argv[8]; // back?
- //reg_t unk9 = argv[9];
- uint16 fontId = argv[10].toUint16();
- //uint16 mode = argv[11].toUint16();
- uint16 dimmed = argv[12].toUint16();
- //warning("kBitmap(4): bitmap ptr %04x:%04x, font %d, mode %d, dimmed %d - text: \"%s\"",
- // PRINT_REG(bitmapPtr), font, mode, dimmed, text.c_str());
- uint16 foreColor = 255; // TODO
-
- byte *memoryPtr = s->_segMan->getHunkPointer(hunkId);
- // Get totalWidth, totalHeight
- uint16 totalWidth = READ_LE_UINT16(memoryPtr);
- uint16 totalHeight = READ_LE_UINT16(memoryPtr + 2);
- byte *bitmap = memoryPtr + BITMAP_HEADER_SIZE;
-
- GfxFont *font = g_sci->_gfxCache->getFont(fontId);
-
- int16 charCount = 0;
- uint16 curX = textX, curY = textY;
- const char *txt = text.c_str();
-
- while (*txt) {
- charCount = g_sci->_gfxText32->GetLongest(txt, totalWidth, font);
- if (charCount == 0)
- break;
-
- for (int i = 0; i < charCount; i++) {
- unsigned char curChar = txt[i];
- font->drawToBuffer(curChar, curY, curX, foreColor, dimmed, bitmap, totalWidth, totalHeight);
- curX += font->getCharWidth(curChar);
- }
-
- curX = textX;
- curY += font->getHeight();
- txt += charCount;
- while (*txt == ' ')
- txt++; // skip over breaking spaces
- }
+ return kStubNull(s, argc + 1, argv - 1);
+}
- }
- break;
- case 5: // fill with color
- {
- // 6 params, called e.g. from TextView::init() and TextView::draw()
- // in Torin's Passage, script 64890
- reg_t hunkId = argv[1]; // obtained from kBitmap(0)
- uint16 x = argv[2].toUint16();
- uint16 y = argv[3].toUint16();
- uint16 fillWidth = argv[4].toUint16(); // width - 1
- uint16 fillHeight = argv[5].toUint16(); // height - 1
- uint16 back = argv[6].toUint16();
-
- byte *memoryPtr = s->_segMan->getHunkPointer(hunkId);
- // Get totalWidth, totalHeight
- uint16 totalWidth = READ_LE_UINT16(memoryPtr);
- uint16 totalHeight = READ_LE_UINT16(memoryPtr + 2);
- uint16 width = MIN<uint16>(totalWidth - x, fillWidth);
- uint16 height = MIN<uint16>(totalHeight - y, fillHeight);
- byte *bitmap = memoryPtr + BITMAP_HEADER_SIZE;
-
- for (uint16 curY = 0; curY < height; curY++) {
- for (uint16 curX = 0; curX < width; curX++) {
- bitmap[(curY + y) * totalWidth + (curX + x)] = back;
- }
- }
+reg_t kBitmapInvert(EngineState *s, int argc, reg_t *argv) {
+ // bitmap, left, top, right, bottom, foreColor, backColor
- }
- break;
- default:
- kStub(s, argc, argv);
- break;
- }
+ return kStubNull(s, argc + 1, argv - 1);
+}
+reg_t kBitmapSetDisplace(EngineState *s, int argc, reg_t *argv) {
+ BitmapResource bitmap(argv[0]);
+ bitmap.setDisplace(Common::Point(argv[1].toSint16(), argv[2].toSint16()));
return s->r_acc;
}
-// Used for edit boxes in save/load dialogs. It's a rewritten version of kEditControl,
-// but it handles events on its own, using an internal loop, instead of using SCI
-// scripts for event management like kEditControl does. Called by script 64914,
-// DEdit::hilite().
-reg_t kEditText(EngineState *s, int argc, reg_t *argv) {
- reg_t controlObject = argv[0];
+reg_t kBitmapCreateFromView(EngineState *s, int argc, reg_t *argv) {
+ // viewId, loopNo, celNo, skipColor, backColor, useRemap, source overlay bitmap
- if (!controlObject.isNull()) {
- g_sci->_gfxControls32->kernelTexteditChange(controlObject);
- }
+ return kStub(s, argc + 1, argv - 1);
+}
- return s->r_acc;
+reg_t kBitmapCopyPixels(EngineState *s, int argc, reg_t *argv) {
+ // target bitmap, source bitmap
+
+ return kStubNull(s, argc + 1, argv - 1);
+}
+
+reg_t kBitmapClone(EngineState *s, int argc, reg_t *argv) {
+ // bitmap
+
+ return kStub(s, argc + 1, argv - 1);
+}
+
+reg_t kBitmapGetInfo(EngineState *s, int argc, reg_t *argv) {
+ // bitmap
+
+ // argc 1 = get width
+ // argc 2 = pixel at row 0 col n
+ // argc 3 = pixel at row n col n
+ return kStub(s, argc + 1, argv - 1);
+}
+
+reg_t kBitmapScale(EngineState *s, int argc, reg_t *argv) {
+ // TODO: SCI3
+ return kStubNull(s, argc + 1, argv - 1);
+}
+
+reg_t kBitmapCreateFromUnknown(EngineState *s, int argc, reg_t *argv) {
+ // TODO: SCI3
+ return kStub(s, argc + 1, argv - 1);
+}
+
+reg_t kEditText(EngineState *s, int argc, reg_t *argv) {
+ return g_sci->_gfxControls32->kernelEditText(argv[0]);
}
reg_t kAddLine(EngineState *s, int argc, reg_t *argv) {
+ return kStubNull(s, argc, argv); // return 0:0 for now, so that follow up calls won't create signature mismatches
+#if 0
reg_t plane = argv[0];
Common::Point startPoint(argv[1].toUint16(), argv[2].toUint16());
Common::Point endPoint(argv[3].toUint16(), argv[4].toUint16());
- // argv[5] is unknown (a number, usually 200)
+ byte priority = (byte)argv[5].toUint16();
byte color = (byte)argv[6].toUint16();
- byte priority = (byte)argv[7].toUint16();
- byte control = (byte)argv[8].toUint16();
- // argv[9] is unknown (usually a small number, 1 or 2). Thickness, perhaps?
- return g_sci->_gfxFrameout->addPlaneLine(plane, startPoint, endPoint, color, priority, control);
+ byte style = (byte)argv[7].toUint16(); // 0: solid, 1: dashed, 2: pattern
+ byte pattern = (byte)argv[8].toUint16();
+ byte thickness = (byte)argv[9].toUint16();
+// return g_sci->_gfxFrameout->addPlaneLine(plane, startPoint, endPoint, color, priority, 0);
+ return s->r_acc;
+#endif
}
reg_t kUpdateLine(EngineState *s, int argc, reg_t *argv) {
+ return kStub(s, argc, argv);
+
+#if 0
reg_t hunkId = argv[0];
reg_t plane = argv[1];
Common::Point startPoint(argv[2].toUint16(), argv[3].toUint16());
@@ -694,14 +729,18 @@ reg_t kUpdateLine(EngineState *s, int argc, reg_t *argv) {
byte priority = (byte)argv[8].toUint16();
byte control = (byte)argv[9].toUint16();
// argv[10] is unknown (usually a small number, 1 or 2). Thickness, perhaps?
- g_sci->_gfxFrameout->updatePlaneLine(plane, hunkId, startPoint, endPoint, color, priority, control);
+// g_sci->_gfxFrameout->updatePlaneLine(plane, hunkId, startPoint, endPoint, color, priority, control);
return s->r_acc;
+#endif
}
reg_t kDeleteLine(EngineState *s, int argc, reg_t *argv) {
+ return kStub(s, argc, argv);
+#if 0
reg_t hunkId = argv[0];
reg_t plane = argv[1];
- g_sci->_gfxFrameout->deletePlaneLine(plane, hunkId);
+// g_sci->_gfxFrameout->deletePlaneLine(plane, hunkId);
return s->r_acc;
+#endif
}
reg_t kSetScroll(EngineState *s, int argc, reg_t *argv) {
@@ -730,13 +769,8 @@ reg_t kSetScroll(EngineState *s, int argc, reg_t *argv) {
// Used by SQ6, script 900, the datacorder reprogramming puzzle (from room 270)
reg_t kMorphOn(EngineState *s, int argc, reg_t *argv) {
- // TODO: g_sci->_gfxManager->palMorphIsOn = true
- // This function sets the palMorphIsOn flag which causes kFrameOut to use
- // an alternative FrameOut function (GraphicsMgr::PalMorphFrameOut instead
- // of GraphicsMgr::FrameOut). At the end of the frame, kFrameOut sets the
- // palMorphIsOn flag back to false.
- kStub(s, argc, argv);
- return NULL_REG;
+ g_sci->_gfxFrameout->_palMorphIsOn = true;
+ return s->r_acc;
}
reg_t kPaletteSetFade(EngineState *s, int argc, reg_t *argv) {
@@ -744,7 +778,7 @@ reg_t kPaletteSetFade(EngineState *s, int argc, reg_t *argv) {
uint16 toColor = argv[1].toUint16();
uint16 percent = argv[2].toUint16();
g_sci->_gfxPalette32->setFade(percent, fromColor, toColor);
- return NULL_REG;
+ return s->r_acc;
}
reg_t kPalVarySetVary(EngineState *s, int argc, reg_t *argv) {
@@ -762,14 +796,14 @@ reg_t kPalVarySetVary(EngineState *s, int argc, reg_t *argv) {
}
g_sci->_gfxPalette32->kernelPalVarySet(paletteId, percent, time, fromColor, toColor);
- return NULL_REG;
+ return s->r_acc;
}
reg_t kPalVarySetPercent(EngineState *s, int argc, reg_t *argv) {
int time = argc > 0 ? argv[0].toSint16() * 60 : 0;
int16 percent = argc > 1 ? argv[1].toSint16() : 0;
g_sci->_gfxPalette32->setVaryPercent(percent, time, -1, -1);
- return NULL_REG;
+ return s->r_acc;
}
reg_t kPalVaryGetPercent(EngineState *s, int argc, reg_t *argv) {
@@ -778,7 +812,7 @@ reg_t kPalVaryGetPercent(EngineState *s, int argc, reg_t *argv) {
reg_t kPalVaryOff(EngineState *s, int argc, reg_t *argv) {
g_sci->_gfxPalette32->varyOff();
- return NULL_REG;
+ return s->r_acc;
}
reg_t kPalVaryMergeTarget(EngineState *s, int argc, reg_t *argv) {
@@ -790,7 +824,7 @@ reg_t kPalVaryMergeTarget(EngineState *s, int argc, reg_t *argv) {
reg_t kPalVarySetTime(EngineState *s, int argc, reg_t *argv) {
int time = argv[0].toSint16() * 60;
g_sci->_gfxPalette32->setVaryTime(time);
- return NULL_REG;
+ return s->r_acc;
}
reg_t kPalVarySetTarget(EngineState *s, int argc, reg_t *argv) {
@@ -875,76 +909,57 @@ reg_t kPalCycle(EngineState *s, int argc, reg_t *argv) {
return s->r_acc;
}
-reg_t kRemapColors32(EngineState *s, int argc, reg_t *argv) {
- uint16 operation = argv[0].toUint16();
+reg_t kRemapColors(EngineState *s, int argc, reg_t *argv) {
+ if (!s)
+ return make_reg(0, getSciVersion());
+ error("not supposed to call this");
+}
- switch (operation) {
- case 0: { // turn remapping off
- // WORKAROUND: Game scripts in QFG4 erroneously turn remapping off in room
- // 140 (the character point allocation screen) and never turn it back on,
- // even if it's clearly used in that screen.
- if (g_sci->getGameId() == GID_QFG4 && s->currentRoomNumber() == 140)
- return s->r_acc;
+reg_t kRemapOff(EngineState *s, int argc, reg_t *argv) {
+ byte color = (argc >= 1) ? argv[0].toUint16() : 0;
+ g_sci->_gfxRemap32->remapOff(color);
+ return s->r_acc;
+}
- int16 base = (argc >= 2) ? argv[1].toSint16() : 0;
- if (base > 0)
- warning("kRemapColors(0) called with base %d", base);
- g_sci->_gfxPalette32->resetRemapping();
- }
- break;
- case 1: { // remap by range
- uint16 color = argv[1].toUint16();
- uint16 from = argv[2].toUint16();
- uint16 to = argv[3].toUint16();
- uint16 base = argv[4].toUint16();
- uint16 unk5 = (argc >= 6) ? argv[5].toUint16() : 0;
- if (unk5 > 0)
- warning("kRemapColors(1) called with 6 parameters, unknown parameter is %d", unk5);
- g_sci->_gfxPalette32->setRemappingRange(color, from, to, base);
- }
- break;
- case 2: { // remap by percent
- uint16 color = argv[1].toUint16();
- uint16 percent = argv[2].toUint16(); // 0 - 100
- if (argc >= 4)
- warning("RemapByPercent called with 4 parameters, unknown parameter is %d", argv[3].toUint16());
- g_sci->_gfxPalette32->setRemappingPercent(color, percent);
- }
- break;
- case 3: { // remap to gray
- // Example call: QFG4 room 490 (Baba Yaga's hut) - params are color 253, 75% and 0.
- // In this room, it's used for the cloud before Baba Yaga appears.
- int16 color = argv[1].toSint16();
- int16 percent = argv[2].toSint16(); // 0 - 100
- if (argc >= 4)
- warning("RemapToGray called with 4 parameters, unknown parameter is %d", argv[3].toUint16());
- g_sci->_gfxPalette32->setRemappingPercentGray(color, percent);
- }
- break;
- case 4: { // remap to percent gray
- // Example call: QFG4 rooms 530/535 (swamp) - params are 253, 100%, 200
- int16 color = argv[1].toSint16();
- int16 percent = argv[2].toSint16(); // 0 - 100
- // argv[3] is unknown (a number, e.g. 200) - start color, perhaps?
- if (argc >= 5)
- warning("RemapToGrayPercent called with 5 parameters, unknown parameter is %d", argv[4].toUint16());
- g_sci->_gfxPalette32->setRemappingPercentGray(color, percent);
- }
- break;
- case 5: { // don't map to range
- //int16 mapping = argv[1].toSint16();
- uint16 intensity = argv[2].toUint16();
- // HACK for PQ4
- if (g_sci->getGameId() == GID_PQ4)
- g_sci->_gfxPalette32->kernelSetIntensity(0, 255, intensity, true);
+reg_t kRemapByRange(EngineState *s, int argc, reg_t *argv) {
+ byte color = argv[0].toUint16();
+ byte from = argv[1].toUint16();
+ byte to = argv[2].toUint16();
+ byte base = argv[3].toUint16();
+ // The last parameter, depth, is unused
+ g_sci->_gfxRemap32->setRemappingRange(color, from, to, base);
+ return s->r_acc;
+}
- kStub(s, argc, argv);
- }
- break;
- default:
- break;
- }
+reg_t kRemapByPercent(EngineState *s, int argc, reg_t *argv) {
+ byte color = argv[0].toUint16();
+ byte percent = argv[1].toUint16();
+ // The last parameter, depth, is unused
+ g_sci->_gfxRemap32->setRemappingPercent(color, percent);
+ return s->r_acc;
+}
+
+reg_t kRemapToGray(EngineState *s, int argc, reg_t *argv) {
+ byte color = argv[0].toUint16();
+ byte gray = argv[1].toUint16();
+ // The last parameter, depth, is unused
+ g_sci->_gfxRemap32->setRemappingToGray(color, gray);
+ return s->r_acc;
+}
+
+reg_t kRemapToPercentGray(EngineState *s, int argc, reg_t *argv) {
+ byte color = argv[0].toUint16();
+ byte gray = argv[1].toUint16();
+ byte percent = argv[2].toUint16();
+ // The last parameter, depth, is unused
+ g_sci->_gfxRemap32->setRemappingToPercentGray(color, gray, percent);
+ return s->r_acc;
+}
+reg_t kRemapSetNoMatchRange(EngineState *s, int argc, reg_t *argv) {
+ byte from = argv[0].toUint16();
+ byte count = argv[1].toUint16();
+ g_sci->_gfxRemap32->setNoMatchRange(from, count);
return s->r_acc;
}
diff --git a/engines/sci/engine/kmisc.cpp b/engines/sci/engine/kmisc.cpp
index 9d47a37bca..f4bb4ff85b 100644
--- a/engines/sci/engine/kmisc.cpp
+++ b/engines/sci/engine/kmisc.cpp
@@ -268,7 +268,10 @@ reg_t kMemory(EngineState *s, int argc, reg_t *argv) {
switch (argv[0].toUint16()) {
case K_MEMORY_ALLOCATE_CRITICAL: {
int byteCount = argv[1].toUint16();
- // WORKAROUND:
+ // Sierra themselves allocated at least 2 bytes more than requested.
+ // Probably as a safety margin. And they also made size even.
+ //
+ // This behavior is required by at least these:
// - pq3 (multilingual) room 202
// when plotting crimes, allocates the returned bytes from kStrLen
// on "W" and "E" and wants to put a string in there, which doesn't
@@ -276,18 +279,22 @@ reg_t kMemory(EngineState *s, int argc, reg_t *argv) {
// - lsl5 (multilingual) room 280
// allocates memory according to a previous kStrLen for the name of
// the airport ladies (bug #3093818), which isn't enough
-
- // We always allocate 1 byte more, because of this
- byteCount++;
+ byteCount += 2 + (byteCount & 1);
if (!s->_segMan->allocDynmem(byteCount, "kMemory() critical", &s->r_acc)) {
error("Critical heap allocation failed");
}
break;
}
- case K_MEMORY_ALLOCATE_NONCRITICAL:
- s->_segMan->allocDynmem(argv[1].toUint16(), "kMemory() non-critical", &s->r_acc);
+ case K_MEMORY_ALLOCATE_NONCRITICAL: {
+ int byteCount = argv[1].toUint16();
+
+ // See above
+ byteCount += 2 + (byteCount & 1);
+
+ s->_segMan->allocDynmem(byteCount, "kMemory() non-critical", &s->r_acc);
break;
+ }
case K_MEMORY_FREE :
if (!s->_segMan->freeDynmem(argv[1])) {
if (g_sci->getGameId() == GID_QFG1VGA) {
diff --git a/engines/sci/engine/kpathing.cpp b/engines/sci/engine/kpathing.cpp
index 5b2245e84d..7ac744f584 100644
--- a/engines/sci/engine/kpathing.cpp
+++ b/engines/sci/engine/kpathing.cpp
@@ -1943,14 +1943,14 @@ static int liesBefore(const Vertex *v, const Common::Point &p1, const Common::Po
// 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;
+ uint32 indexw1;
+ uint32 indexp1;
const Vertex *vertexw1;
const Vertex *vertexp1;
Common::Point intersection1;
- unsigned int indexw2;
- unsigned int indexp2;
+ uint32 indexw2;
+ uint32 indexp2;
const Vertex *vertexw2;
const Vertex *vertexp2;
Common::Point intersection2;
@@ -1960,7 +1960,7 @@ struct Patch {
// Check if the given vertex on the work polygon is bypassed by this patch.
-static bool isVertexCovered(const Patch &p, unsigned int wi) {
+static bool isVertexCovered(const Patch &p, uint32 wi) {
// / v (outside)
// ---w1--1----p----w2--2----
@@ -2402,7 +2402,7 @@ reg_t kMergePoly(EngineState *s, int argc, reg_t *argv) {
// Copy work.vertices into arrayRef
Vertex *vertex;
- unsigned int n = 0;
+ uint32 n = 0;
CLIST_FOREACH(vertex, &work.vertices) {
if (vertex == work.vertices._head || vertex->v != vertex->_prev->v)
writePoint(arrayRef, n++, vertex->v);
diff --git a/engines/sci/engine/ksound.cpp b/engines/sci/engine/ksound.cpp
index 032191e4c1..398a623286 100644
--- a/engines/sci/engine/ksound.cpp
+++ b/engines/sci/engine/ksound.cpp
@@ -206,8 +206,15 @@ reg_t kDoAudio(EngineState *s, int argc, reg_t *argv) {
// athrxx: It seems from disasm that the original KQ5 FM-Towns loads a default language (Japanese) audio map at the beginning
// right after loading the video and audio drivers. The -1 language argument in here simply means that the original will stick
// with Japanese. Instead of doing that we switch to the language selected in the launcher.
- if (g_sci->getPlatform() == Common::kPlatformFMTowns && language == -1)
- language = (g_sci->getLanguage() == Common::JA_JPN) ? K_LANG_JAPANESE : K_LANG_ENGLISH;
+ if (g_sci->getPlatform() == Common::kPlatformFMTowns && language == -1) {
+ // FM-Towns calls us to get the current language / also set the default language
+ // This doesn't just happen right at the start, but also when the user clicks on the Sierra logo in the game menu
+ // It uses the result of this call to either show "English Voices" or "Japanese Voices".
+
+ // Language should have been set by setLauncherLanguage() already (or could have been modified by the scripts).
+ // Get this language setting, so that the chosen language will get set for resource manager.
+ language = g_sci->getSciLanguage();
+ }
debugC(kDebugLevelSound, "kDoAudio: set language to %d", language);
@@ -233,19 +240,38 @@ reg_t kDoAudio(EngineState *s, int argc, reg_t *argv) {
#endif
}
- // 3 new subops in Pharkas. kDoAudio in Pharkas sits at seg026:038C
+ // 3 new subops in Pharkas CD (including CD demo). kDoAudio in Pharkas sits at seg026:038C
case 11:
// Not sure where this is used yet
warning("kDoAudio: Unhandled case 11, %d extra arguments passed", argc - 1);
break;
case 12:
- // Seems to be some sort of audio sync, used in Pharkas. Silenced the
- // warning due to the high level of spam it produces. (takes no params)
- //warning("kDoAudio: Unhandled case 12, %d extra arguments passed", argc - 1);
+ // SSCI calls this function with no parameters from
+ // the TalkRandCycle class and branches on the return
+ // value like a boolean. The conjectured purpose of
+ // this function is to ensure that the talker's mouth
+ // does not move if there is read jitter (slow CD
+ // drive, scratched CD). The old behavior here of not
+ // doing anything caused a nonzero value to be left in
+ // the accumulator by chance. This is equivalent, but
+ // more explicit.
+
+ return make_reg(0, 1);
break;
case 13:
- // Used in Pharkas whenever a speech sample starts (takes no params)
- //warning("kDoAudio: Unhandled case 13, %d extra arguments passed", argc - 1);
+ // SSCI returns a serial number for the played audio
+ // here, used in the PointsSound class. The reason is severalfold:
+
+ // 1. SSCI does not support multiple wave effects at once
+ // 2. FPFP may disable its icon bar during the points sound.
+ // 3. Each new sound preempts any sound already playing.
+ // 4. If the points sound is interrupted before completion,
+ // the icon bar could remain disabled.
+
+ // Since points (1) and (3) do not apply to us, we can simply
+ // return a constant here. This is equivalent to the
+ // old behavior, as above.
+ return make_reg(0, 1);
break;
case 17:
// Seems to be some sort of audio sync, used in SQ6. Silenced the
diff --git a/engines/sci/engine/kstring.cpp b/engines/sci/engine/kstring.cpp
index 310e38dbd1..1c08bf597c 100644
--- a/engines/sci/engine/kstring.cpp
+++ b/engines/sci/engine/kstring.cpp
@@ -661,19 +661,6 @@ reg_t kStrSplit(EngineState *s, int argc, reg_t *argv) {
#ifdef ENABLE_SCI32
-reg_t kText(EngineState *s, int argc, reg_t *argv) {
- switch (argv[0].toUint16()) {
- case 0:
- return kTextSize(s, argc - 1, argv + 1);
- default:
- // TODO: Other subops here too, perhaps kTextColors and kTextFonts
- warning("kText(%d)", argv[0].toUint16());
- break;
- }
-
- return s->r_acc;
-}
-
// TODO: there is an unused second argument, happens at least in LSL6 right during the intro
reg_t kStringNew(EngineState *s, int argc, reg_t *argv) {
reg_t stringHandle;
@@ -778,11 +765,14 @@ reg_t kStringCopy(EngineState *s, int argc, reg_t *argv) {
}
// The original engine ignores bad copies too
- if (index2 > string2Size)
+ if (index2 >= string2Size)
return NULL_REG;
// A count of -1 means fill the rest of the array
- uint32 count = argv[4].toSint16() == -1 ? string2Size - index2 + 1 : argv[4].toUint16();
+ uint32 count = string2Size - index2;
+ if (argv[4].toSint16() != -1) {
+ count = MIN(count, (uint32)argv[4].toUint16());
+ }
// reg_t strAddress = argv[0];
SciString *string1 = s->_segMan->lookupString(argv[0]);
diff --git a/engines/sci/engine/object.cpp b/engines/sci/engine/object.cpp
index 0626c084c1..0566d6955f 100644
--- a/engines/sci/engine/object.cpp
+++ b/engines/sci/engine/object.cpp
@@ -255,6 +255,8 @@ void Object::initSelectorsSci3(const byte *buf) {
if (g_sci->getKernel()->getSelectorNamesSize() % 32)
++groups;
+ _mustSetViewVisible.resize(groups);
+
methods = properties = 0;
// Selectors are divided into groups of 32, of which the first
@@ -270,7 +272,9 @@ void Object::initSelectorsSci3(const byte *buf) {
// This object actually has selectors belonging to this group
int typeMask = READ_SCI11ENDIAN_UINT32(seeker);
- for (int bit = 2; bit < 32; ++bit) {
+ _mustSetViewVisible[groupNr] = (typeMask & 1);
+
+ for (int bit = 2; bit < 32; ++bit) {
int value = READ_SCI11ENDIAN_UINT16(seeker + bit * 2);
if (typeMask & (1 << bit)) { // Property
++properties;
@@ -281,7 +285,8 @@ void Object::initSelectorsSci3(const byte *buf) {
}
}
- }
+ } else
+ _mustSetViewVisible[groupNr] = false;
}
_variables.resize(properties);
diff --git a/engines/sci/engine/object.h b/engines/sci/engine/object.h
index 0ae7ed2cab..a7be170f4f 100644
--- a/engines/sci/engine/object.h
+++ b/engines/sci/engine/object.h
@@ -41,8 +41,21 @@ enum {
};
enum infoSelectorFlags {
- kInfoFlagClone = 0x0001,
- kInfoFlagClass = 0x8000
+ kInfoFlagClone = 0x0001,
+#ifdef ENABLE_SCI32
+ /**
+ * When set, indicates to game scripts that a screen
+ * item can be updated.
+ */
+ kInfoFlagViewVisible = 0x0008, // TODO: "dirty" ?
+
+ /**
+ * When set, the object has an associated screen item in
+ * the rendering tree.
+ */
+ kInfoFlagViewInserted = 0x0010,
+#endif
+ kInfoFlagClass = 0x8000
};
enum ObjectOffsets {
@@ -120,7 +133,24 @@ public:
_infoSelectorSci3 = info;
}
- // No setter for the -info- selector
+#ifdef ENABLE_SCI32
+ void setInfoSelectorFlag(infoSelectorFlags flag) {
+ if (getSciVersion() < SCI_VERSION_3) {
+ _variables[_offset + 2] |= flag;
+ } else {
+ _infoSelectorSci3 |= flag;
+ }
+ }
+
+ // NOTE: In real engine, -info- is treated as byte size
+ void clearInfoSelectorFlag(infoSelectorFlags flag) {
+ if (getSciVersion() < SCI_VERSION_3) {
+ _variables[_offset + 2] &= ~flag;
+ } else {
+ _infoSelectorSci3 &= ~flag;
+ }
+ }
+#endif
reg_t getNameSelector() const {
if (getSciVersion() < SCI_VERSION_3)
@@ -232,6 +262,8 @@ public:
bool initBaseObject(SegManager *segMan, reg_t addr, bool doInitSuperClass = true);
void syncBaseObject(const byte *ptr) { _baseObj = ptr; }
+ bool mustSetViewVisibleSci3(int selector) const { return _mustSetViewVisible[selector/32]; }
+
private:
void initSelectorsSci3(const byte *buf);
@@ -248,6 +280,7 @@ private:
reg_t _superClassPosSci3; /**< reg_t pointing to superclass for SCI3 */
reg_t _speciesSelectorSci3; /**< reg_t containing species "selector" for SCI3 */
reg_t _infoSelectorSci3; /**< reg_t containing info "selector" for SCI3 */
+ Common::Array<bool> _mustSetViewVisible; /** cached bit of info to make lookup fast, SCI3 only */
};
diff --git a/engines/sci/engine/savegame.cpp b/engines/sci/engine/savegame.cpp
index 165c147c1c..fcb65157d8 100644
--- a/engines/sci/engine/savegame.cpp
+++ b/engines/sci/engine/savegame.cpp
@@ -39,6 +39,7 @@
#include "sci/engine/vm_types.h"
#include "sci/engine/script.h" // for SCI_OBJ_EXPORTS and SCI_OBJ_SYNONYMS
#include "sci/graphics/helpers.h"
+#include "sci/graphics/menu.h"
#include "sci/graphics/palette.h"
#include "sci/graphics/ports.h"
#include "sci/graphics/screen.h"
@@ -957,7 +958,8 @@ bool gamestate_save(EngineState *s, Common::WriteStream *fh, const Common::Strin
extern void showScummVMDialog(const Common::String &message);
void gamestate_delayedrestore(EngineState *s) {
- Common::String fileName = g_sci->getSavegameName(s->_delayedRestoreGameId);
+ int savegameId = s->_delayedRestoreGameId; // delayedRestoreGameId gets destroyed within gamestate_restore()!
+ Common::String fileName = g_sci->getSavegameName(savegameId);
Common::SeekableReadStream *in = g_sci->getSaveFileManager()->openForLoading(fileName);
if (in) {
@@ -965,6 +967,7 @@ void gamestate_delayedrestore(EngineState *s) {
gamestate_restore(s, in);
delete in;
if (s->r_acc != make_reg(0, 1)) {
+ gamestate_afterRestoreFixUp(s, savegameId);
return;
}
}
@@ -972,6 +975,73 @@ void gamestate_delayedrestore(EngineState *s) {
error("Restoring gamestate '%s' failed", fileName.c_str());
}
+void gamestate_afterRestoreFixUp(EngineState *s, int savegameId) {
+ switch (g_sci->getGameId()) {
+ case GID_MOTHERGOOSE:
+ // WORKAROUND: Mother Goose SCI0
+ // Script 200 / rm200::newRoom will set global C5h directly right after creating a child to the
+ // current number of children plus 1.
+ // We can't trust that global, that's why we set the actual savedgame id right here directly after
+ // restoring a saved game.
+ // If we didn't, the game would always save to a new slot
+ s->variables[VAR_GLOBAL][0xC5].setOffset(SAVEGAMEID_OFFICIALRANGE_START + savegameId);
+ break;
+ case GID_MOTHERGOOSE256:
+ // WORKAROUND: Mother Goose SCI1/SCI1.1 does some weird things for
+ // saving a previously restored game.
+ // We set the current savedgame-id directly and remove the script
+ // code concerning this via script patch.
+ s->variables[VAR_GLOBAL][0xB3].setOffset(SAVEGAMEID_OFFICIALRANGE_START + savegameId);
+ break;
+ case GID_JONES:
+ // HACK: The code that enables certain menu items isn't called when a game is restored from the
+ // launcher, or the "Restore game" option in the game's main menu - bugs #6537 and #6723.
+ // These menu entries are disabled when the game is launched, and are enabled when a new game is
+ // started. The code for enabling these entries is is all in script 1, room1::init, but that code
+ // path is never followed in these two cases (restoring game from the menu, or restoring a game
+ // from the ScummVM launcher). Thus, we perform the calls to enable the menus ourselves here.
+ // These two are needed when restoring from the launcher
+ // FIXME: The original interpreter saves and restores the menu state, so these attributes
+ // are automatically reset there. We may want to do the same.
+ g_sci->_gfxMenu->kernelSetAttribute(257 >> 8, 257 & 0xFF, SCI_MENU_ATTRIBUTE_ENABLED, TRUE_REG); // Sierra -> About Jones
+ g_sci->_gfxMenu->kernelSetAttribute(258 >> 8, 258 & 0xFF, SCI_MENU_ATTRIBUTE_ENABLED, TRUE_REG); // Sierra -> Help
+ // The rest are normally enabled from room1::init
+ g_sci->_gfxMenu->kernelSetAttribute(769 >> 8, 769 & 0xFF, SCI_MENU_ATTRIBUTE_ENABLED, TRUE_REG); // Options -> Delete current player
+ g_sci->_gfxMenu->kernelSetAttribute(513 >> 8, 513 & 0xFF, SCI_MENU_ATTRIBUTE_ENABLED, TRUE_REG); // Game -> Save Game
+ g_sci->_gfxMenu->kernelSetAttribute(515 >> 8, 515 & 0xFF, SCI_MENU_ATTRIBUTE_ENABLED, TRUE_REG); // Game -> Restore Game
+ g_sci->_gfxMenu->kernelSetAttribute(1025 >> 8, 1025 & 0xFF, SCI_MENU_ATTRIBUTE_ENABLED, TRUE_REG); // Status -> Statistics
+ g_sci->_gfxMenu->kernelSetAttribute(1026 >> 8, 1026 & 0xFF, SCI_MENU_ATTRIBUTE_ENABLED, TRUE_REG); // Status -> Goals
+ break;
+ case GID_KQ6:
+ if (g_sci->isCD()) {
+ // WORKAROUND:
+ // For the CD version of King's Quest 6, set global depending on current hires/lowres state
+ // The game sets a global at the start depending on it and some things check that global
+ // instead of checking platform like for example the game action menu.
+ // This never happened in the original interpreter, because the original DOS interpreter
+ // was only capable of lowres graphics and the original Windows 3.11 interpreter was only capable
+ // of hires graphics. Saved games were not compatible between those two.
+ // Which means saving during lowres mode, then going into hires mode and restoring that saved game,
+ // will result in some graphics being incorrect (lowres).
+ // That's why we are setting the global after restoring a saved game depending on hires/lowres state.
+ // The CD demo of KQ6 does the same and uses the exact same global.
+ if ((g_sci->getPlatform() == Common::kPlatformWindows) || (g_sci->forceHiresGraphics())) {
+ s->variables[VAR_GLOBAL][0xA9].setOffset(1);
+ } else {
+ s->variables[VAR_GLOBAL][0xA9].setOffset(0);
+ }
+ }
+ break;
+ case GID_PQ2:
+ // HACK: Same as above - enable the save game menu option when loading in PQ2 (bug #6875).
+ // It gets disabled in the game's death screen.
+ g_sci->_gfxMenu->kernelSetAttribute(2, 1, SCI_MENU_ATTRIBUTE_ENABLED, TRUE_REG); // Game -> Save Game
+ break;
+ default:
+ break;
+ }
+}
+
void gamestate_restore(EngineState *s, Common::SeekableReadStream *fh) {
SavegameMetadata meta;
@@ -1013,13 +1083,31 @@ void gamestate_restore(EngineState *s, Common::SeekableReadStream *fh) {
if (g_sci->_gfxPorts)
g_sci->_gfxPorts->reset();
// clear screen
- if (g_sci->_gfxScreen)
- g_sci->_gfxScreen->clearForRestoreGame();
+ if (getSciVersion() <= SCI_VERSION_1_1) {
+ // Only do clearing the screen for SCI16
+ // Both SCI16 + SCI32 did not clear the screen.
+ // We basically do it for SCI16, because of KQ6.
+ // When hires portraits are shown and the user restores during that time, the portraits
+ // wouldn't get fully removed. In original SCI, the user wasn't able to restore during that time,
+ // so this is basically a workaround, so that ScummVM features work properly.
+ // For SCI32, behavior was verified in DOSBox, that SCI32 does not clear and also not redraw the screen.
+ // It only redraws elements that have changed in comparison to the state before the restore.
+ // If we cleared the screen for SCI32, we would have issues because of this behavior.
+ if (g_sci->_gfxScreen)
+ g_sci->_gfxScreen->clearForRestoreGame();
+ }
#ifdef ENABLE_SCI32
- // Also clear any SCI32 planes/screen items currently showing so they
- // don't show up after the load.
- if (getSciVersion() >= SCI_VERSION_2)
- g_sci->_gfxFrameout->clear();
+ // Delete current planes/elements of actively loaded VM, only when our ScummVM dialogs are patched in
+ // We MUST NOT delete all planes/screen items. At least Space Quest 6 has a few in memory like for example
+ // the options plane, which are not re-added and are in memory all the time right from the start of the
+ // game. Sierra SCI32 did not clear planes, only scripts cleared the ones inside planes::elements.
+ if (getSciVersion() >= SCI_VERSION_2) {
+ if (!s->_delayedRestoreFromLauncher) {
+ // Only do it, when we are restoring regulary and not from launcher
+ // As it could result in option planes etc. on the screen (happens in gk1)
+ g_sci->_gfxFrameout->syncWithScripts(false);
+ }
+ }
#endif
s->reset(true);
@@ -1044,6 +1132,13 @@ void gamestate_restore(EngineState *s, Common::SeekableReadStream *fh) {
if (g_sci->_gfxPorts)
g_sci->_gfxPorts->saveLoadWithSerializer(ser);
+ // SCI32:
+ // Current planes/screen elements of freshly loaded VM are re-added by scripts in [gameID]::replay
+ // We don't have to do that in here.
+ // But we may have to do it ourselves in case we ever implement some soft-error handling in case
+ // a saved game can't be restored. That way we can restore the game screen.
+ // see _gfxFrameout->syncWithScripts()
+
Vocabulary *voc = g_sci->getVocabulary();
if (ser.getVersion() >= 30 && voc)
voc->saveLoadWithSerializer(ser);
@@ -1061,6 +1156,8 @@ void gamestate_restore(EngineState *s, Common::SeekableReadStream *fh) {
// signal restored game to game scripts
s->gameIsRestarting = GAMEISRESTARTING_RESTORE;
+
+ s->_delayedRestoreFromLauncher = false;
}
bool get_savegame_metadata(Common::SeekableReadStream *stream, SavegameMetadata *meta) {
diff --git a/engines/sci/engine/savegame.h b/engines/sci/engine/savegame.h
index bb555434c9..459e992e24 100644
--- a/engines/sci/engine/savegame.h
+++ b/engines/sci/engine/savegame.h
@@ -87,6 +87,9 @@ bool gamestate_save(EngineState *s, Common::WriteStream *save, const Common::Str
// does a delayed saved game restore, used by ScummVM game menu - see detection.cpp / SciEngine::loadGameState()
void gamestate_delayedrestore(EngineState *s);
+// does a few fixups right after restoring a saved game
+void gamestate_afterRestoreFixUp(EngineState *s, int savegameId);
+
/**
* Restores a game state from a directory.
* @param s An older state from the same game
diff --git a/engines/sci/engine/script_patches.cpp b/engines/sci/engine/script_patches.cpp
index 6915e12a0e..8039c5f282 100644
--- a/engines/sci/engine/script_patches.cpp
+++ b/engines/sci/engine/script_patches.cpp
@@ -101,6 +101,8 @@ static const char *const selectorNameTable[] = {
"startText", // King's Quest 6 CD / Laura Bow 2 CD for audio+text support
"startAudio", // King's Quest 6 CD / Laura Bow 2 CD for audio+text support
"modNum", // King's Quest 6 CD / Laura Bow 2 CD for audio+text support
+ "cycler", // Space Quest 4 / system selector
+ "setLoop", // Laura Bow 1 Colonel's Bequest
NULL
};
@@ -127,7 +129,9 @@ enum ScriptPatcherSelectors {
SELECTOR_timesShownID,
SELECTOR_startText,
SELECTOR_startAudio,
- SELECTOR_modNum
+ SELECTOR_modNum,
+ SELECTOR_cycler,
+ SELECTOR_setLoop
};
// ===========================================================================
@@ -404,6 +408,59 @@ static const SciScriptPatcherEntry fanmadeSignatures[] = {
};
// ===========================================================================
+
+// WORKAROUND
+// Freddy Pharkas intro screen
+// Sierra used inner loops for the scaling of the 2 title views.
+// Those inner loops don't call kGameIsRestarting, which is why
+// we do not update the screen and we also do not throttle.
+//
+// This patch fixes this and makes it work.
+// Applies to at least: English PC-CD
+// Responsible method: sTownScript::changeState(1), sTownScript::changeState(3) (script 110)
+static const uint16 freddypharkasSignatureIntroScaling[] = {
+ 0x38, SIG_ADDTOOFFSET(+2), // pushi (setLoop) (009b for PC CD)
+ 0x78, // push1
+ PATCH_ADDTOOFFSET(1), // push0 for first code, push1 for second code
+ 0x38, SIG_ADDTOOFFSET(+2), // pushi (setStep) (0143 for PC CD)
+ 0x7a, // push2
+ 0x39, 0x05, // pushi 05
+ 0x3c, // dup
+ 0x72, SIG_ADDTOOFFSET(+2), // lofsa (view)
+ SIG_MAGICDWORD,
+ 0x4a, 0x1e, // send 1e
+ 0x35, 0x0a, // ldi 0a
+ 0xa3, 0x02, // sal local[2]
+ // start of inner loop
+ 0x8b, 0x02, // lsl local[2]
+ SIG_ADDTOOFFSET(+43), // skip almost all of inner loop
+ 0xa3, 0x02, // sal local[2]
+ 0x33, 0xcf, // jmp [inner loop start]
+ SIG_END
+};
+
+static const uint16 freddypharkasPatchIntroScaling[] = {
+ // remove setLoop(), objects in heap are already prepared, saves 5 bytes
+ 0x38,
+ PATCH_GETORIGINALBYTE(+6),
+ PATCH_GETORIGINALBYTE(+7), // pushi (setStep)
+ 0x7a, // push2
+ 0x39, 0x05, // pushi 05
+ 0x3c, // dup
+ 0x72,
+ PATCH_GETORIGINALBYTE(+13),
+ PATCH_GETORIGINALBYTE(+14), // lofsa (view)
+ 0x4a, 0x18, // send 18 - adjusted
+ 0x35, 0x0a, // ldi 0a
+ 0xa3, 0x02, // sal local[2]
+ // start of new inner loop
+ 0x39, 0x00, // pushi 00
+ 0x43, 0x2c, 0x00, // callk GameIsRestarting <-- add this so that our speed throttler is triggered
+ SIG_ADDTOOFFSET(+47), // skip almost all of inner loop
+ 0x33, 0xca, // jmp [inner loop start]
+ PATCH_END
+};
+
// script 0 of freddy pharkas/CD PointsSound::check waits for a signal and if
// no signal received will call kDoSound(0xD) which is a dummy in sierra sci
// and ScummVM and will use acc (which is not set by the dummy) to trigger
@@ -522,6 +579,7 @@ static const uint16 freddypharkasPatchMacInventory[] = {
static const SciScriptPatcherEntry freddypharkasSignatures[] = {
{ true, 0, "CD: score early disposal", 1, freddypharkasSignatureScoreDisposal, freddypharkasPatchScoreDisposal },
{ true, 15, "Mac: broken inventory", 1, freddypharkasSignatureMacInventory, freddypharkasPatchMacInventory },
+ { true, 110, "intro scaling workaround", 2, freddypharkasSignatureIntroScaling, freddypharkasPatchIntroScaling },
{ true, 235, "CD: canister pickup hang", 3, freddypharkasSignatureCanisterHang, freddypharkasPatchCanisterHang },
{ true, 320, "ladder event issue", 2, freddypharkasSignatureLadderEvent, freddypharkasPatchLadderEvent },
SCI_SIGNATUREENTRY_TERMINATOR
@@ -755,6 +813,32 @@ static const uint16 kq5PatchWitchCageInit[] = {
PATCH_END
};
+// The multilingual releases of KQ5 hang right at the end during the magic battle with Mordack.
+// It seems additional code was added to wait for signals, but the signals are never set and thus
+// the game hangs. We disable that code, so that the battle works again.
+// This also happened in the original interpreter.
+// We must not change similar code, that happens before.
+
+// Applies to at least: French PC floppy, German PC floppy, Spanish PC floppy
+// Responsible method: stingScript::changeState, dragonScript::changeState, snakeScript::changeState
+static const uint16 kq5SignatureMultilingualEndingGlitch[] = {
+ SIG_MAGICDWORD,
+ 0x89, 0x57, // lsg global[57h]
+ 0x35, 0x00, // ldi 0
+ 0x1a, // eq?
+ 0x18, // not
+ 0x30, SIG_UINT16(0x0011), // bnt [skip signal check]
+ SIG_ADDTOOFFSET(+8), // skip globalSound::prevSignal get code
+ 0x36, // push
+ 0x35, 0x0a, // ldi 0Ah
+ SIG_END
+};
+
+static const uint16 kq5PatchMultilingualEndingGlitch[] = {
+ PATCH_ADDTOOFFSET(+6),
+ 0x32, // change BNT into JMP
+ PATCH_END
+};
// In the final battle, the DOS version uses signals in the music to handle
// timing, while in the Windows version another method is used and the GM
@@ -785,9 +869,10 @@ static const uint16 kq5PatchWinGMSignals[] = {
// script, description, signature patch
static const SciScriptPatcherEntry kq5Signatures[] = {
- { true, 0, "CD: harpy volume change", 1, kq5SignatureCdHarpyVolume, kq5PatchCdHarpyVolume },
- { true, 200, "CD: witch cage init", 1, kq5SignatureWitchCageInit, kq5PatchWitchCageInit },
- { false, 124, "Win: GM Music signal checks", 4, kq5SignatureWinGMSignals, kq5PatchWinGMSignals },
+ { true, 0, "CD: harpy volume change", 1, kq5SignatureCdHarpyVolume, kq5PatchCdHarpyVolume },
+ { true, 200, "CD: witch cage init", 1, kq5SignatureWitchCageInit, kq5PatchWitchCageInit },
+ { true, 124, "Multilingual: Ending glitching out", 3, kq5SignatureMultilingualEndingGlitch, kq5PatchMultilingualEndingGlitch },
+ { false, 124, "Win: GM Music signal checks", 4, kq5SignatureWinGMSignals, kq5PatchWinGMSignals },
SCI_SIGNATUREENTRY_TERMINATOR
};
@@ -1201,8 +1286,10 @@ static const uint16 kq6CDPatchAudioTextSupportJumpAlways[] = {
};
// Fixes "Girl In The Tower" to get played in dual mode as well
+// Also changes credits to use CD audio for dual mode.
+//
// Applies to at least: PC-CD
-// Patched method: rm740::cue
+// Patched method: rm740::cue (script 740), sCredits::init (script 52)
static const uint16 kq6CDSignatureAudioTextSupportGirlInTheTower[] = {
SIG_MAGICDWORD,
0x89, 0x5a, // lsg global[5a]
@@ -1329,6 +1416,7 @@ static const SciScriptPatcherEntry kq6Signatures[] = {
{ false, 928, "CD: audio + text support KQ6 4", 1, kq6CDSignatureAudioTextSupport4, kq6CDPatchAudioTextSupport4 },
{ false, 1009, "CD: audio + text support KQ6 Guards", 2, kq6CDSignatureAudioTextSupportGuards, kq6CDPatchAudioTextSupportGuards },
{ false, 1027, "CD: audio + text support KQ6 Stepmother", 1, kq6CDSignatureAudioTextSupportStepmother, kq6CDPatchAudioTextSupportJumpAlways },
+ { false, 52, "CD: audio + text support KQ6 Girl In The Tower", 1, kq6CDSignatureAudioTextSupportGirlInTheTower, kq6CDPatchAudioTextSupportGirlInTheTower },
{ false, 740, "CD: audio + text support KQ6 Girl In The Tower", 1, kq6CDSignatureAudioTextSupportGirlInTheTower, kq6CDPatchAudioTextSupportGirlInTheTower },
{ false, 370, "CD: audio + text support KQ6 Azure & Ariel", 6, kq6CDSignatureAudioTextSupportAzureAriel, kq6CDPatchAudioTextSupportAzureAriel },
{ false, 903, "CD: audio + text support KQ6 menu", 1, kq6CDSignatureAudioTextMenuSupport, kq6CDPatchAudioTextMenuSupport },
@@ -1536,6 +1624,43 @@ static const SciScriptPatcherEntry larry6Signatures[] = {
};
// ===========================================================================
+// Laura Bow 1 - Colonel's Bequest
+//
+// This is basically just a broken easter egg in Colonel's Bequest.
+// A plane can show up in room 4, but that only happens really rarely.
+// Anyway the Sierra developer seems to have just entered the wrong loop,
+// which is why the statue view is used instead (loop 0).
+// We fix it to use the correct loop.
+//
+// This is only broken in the PC version. It was fixed for Amiga + Atari ST.
+//
+// Credits to OmerMor, for finding it.
+
+// Applies to at least: English PC Floppy
+// Responsible method: room4::init
+static const uint16 laurabow1SignatureEasterEggViewFix[] = {
+ 0x78, // push1
+ 0x76, // push0
+ SIG_MAGICDWORD,
+ 0x38, SIG_SELECTOR16(setLoop), // pushi "setLoop"
+ 0x78, // push1
+ 0x39, 0x03, // pushi 3 (loop 3, view only has 3 loops)
+ SIG_END
+};
+
+static const uint16 laurabow1PatchEasterEggViewFix[] = {
+ PATCH_ADDTOOFFSET(+7),
+ 0x02, // change loop to 2
+ PATCH_END
+};
+
+// script, description, signature patch
+static const SciScriptPatcherEntry laurabow1Signatures[] = {
+ { true, 4, "easter egg view fix", 1, laurabow1SignatureEasterEggViewFix, laurabow1PatchEasterEggViewFix },
+ SCI_SIGNATUREENTRY_TERMINATOR
+};
+
+// ===========================================================================
// Laura Bow 2
//
// Moving away the painting in the room with the hidden safe is problematic
@@ -1840,15 +1965,103 @@ static const SciScriptPatcherEntry laurabow2Signatures[] = {
// MG::replay somewhat calculates the savedgame-id used when saving again
// this doesn't work right and we remove the code completely.
// We set the savedgame-id directly right after restoring in kRestoreGame.
+// We also draw the background picture in here instead.
+// This Mixed Up Mother Goose draws the background picture before restoring,
+// instead of doing it properly in MG::replay. This fixes graphic issues,
+// when restoring from GMM.
+//
+// Applies to at least: English SCI1 CD, English SCI1.1 floppy, Japanese FM-Towns
+// Responsible method: MG::replay (script 0)
static const uint16 mothergoose256SignatureReplay[] = {
+ 0x7a, // push2
+ 0x78, // push1
+ 0x5b, 0x00, 0xbe, // lea global[BEh]
+ 0x36, // push
+ 0x43, 0x70, 0x04, // callk MemorySegment
+ 0x7a, // push2
+ 0x5b, 0x00, 0xbe, // lea global[BEh]
+ 0x36, // push
+ 0x76, // push0
+ 0x43, 0x62, 0x04, // callk StrAt
+ 0xa1, 0xaa, // sag global[AAh]
+ 0x7a, // push2
+ 0x5b, 0x00, 0xbe, // lea global[BEh]
+ 0x36, // push
+ 0x78, // push1
+ 0x43, 0x62, 0x04, // callk StrAt
+ 0x36, // push
+ 0x35, 0x20, // ldi 20
+ 0x04, // sub
+ 0xa1, SIG_ADDTOOFFSET(+1), // sag global[57h] -> FM-Towns [9Dh]
+ // 35 bytes
+ 0x39, 0x03, // pushi 03
+ 0x89, SIG_ADDTOOFFSET(+1), // lsg global[1Dh] -> FM-Towns [1Eh]
+ 0x76, // push0
+ 0x7a, // push2
+ 0x5b, 0x00, 0xbe, // lea global[BEh]
+ 0x36, // push
+ 0x7a, // push2
+ 0x43, 0x62, 0x04, // callk StrAt
+ 0x36, // push
+ 0x35, 0x01, // ldi 01
+ 0x04, // sub
+ 0x36, // push
+ 0x43, 0x62, 0x06, // callk StrAt
+ // 22 bytes
+ 0x7a, // push2
+ 0x5b, 0x00, 0xbe, // lea global[BE]
+ 0x36, // push
+ 0x39, 0x03, // pushi 03
+ 0x43, 0x62, 0x04, // callk StrAt
+ // 10 bytes
0x36, // push
0x35, SIG_MAGICDWORD, 0x20, // ldi 20
0x04, // sub
0xa1, 0xb3, // sag global[b3]
+ // 6 bytes
SIG_END
};
static const uint16 mothergoose256PatchReplay[] = {
+ 0x39, 0x06, // pushi 06
+ 0x76, // push0
+ 0x76, // push0
+ 0x38, PATCH_UINT16(200), // pushi 200d
+ 0x38, PATCH_UINT16(320), // pushi 320d
+ 0x76, // push0
+ 0x76, // push0
+ 0x43, 0x15, 0x0c, // callk SetPort -> set picture port to full screen
+ // 15 bytes
+ 0x39, 0x04, // pushi 04
+ 0x3c, // dup
+ 0x76, // push0
+ 0x38, PATCH_UINT16(255), // pushi 255d
+ 0x76, // push0
+ 0x43, 0x6f, 0x08, // callk Palette -> set intensity to 0 for all colors
+ // 11 bytes
+ 0x7a, // push2
+ 0x38, PATCH_UINT16(800), // pushi 800
+ 0x76, // push0
+ 0x43, 0x08, 0x04, // callk DrawPic -> draw picture 800
+ // 8 bytes
+ 0x39, 0x06, // pushi 06
+ 0x39, 0x0c, // pushi 0Ch
+ 0x76, // push0
+ 0x76, // push0
+ 0x38, PATCH_UINT16(200), // push 200
+ 0x38, PATCH_UINT16(320), // push 320
+ 0x78, // push1
+ 0x43, 0x6c, 0x0c, // callk Graph -> send everything to screen
+ // 16 bytes
+ 0x39, 0x06, // pushi 06
+ 0x76, // push0
+ 0x76, // push0
+ 0x38, PATCH_UINT16(156), // pushi 156d
+ 0x38, PATCH_UINT16(258), // pushi 258d
+ 0x39, 0x03, // pushi 03
+ 0x39, 0x04, // pushi 04
+ 0x43, 0x15, 0x0c, // callk SetPort -> set picture port back
+ // 17 bytes
0x34, PATCH_UINT16(0x0000), // ldi 0000 (dummy)
0x34, PATCH_UINT16(0x0000), // ldi 0000 (dummy)
PATCH_END
@@ -1856,6 +2069,9 @@ static const uint16 mothergoose256PatchReplay[] = {
// when saving, it also checks if the savegame ID is below 13.
// we change this to check if below 113 instead
+//
+// Applies to at least: English SCI1 CD, English SCI1.1 floppy, Japanese FM-Towns
+// Responsible method: Game::save (script 994 for SCI1), MG::save (script 0 for SCI1.1)
static const uint16 mothergoose256SignatureSaveLimit[] = {
0x89, SIG_MAGICDWORD, 0xb3, // lsg global[b3]
0x35, 0x0d, // ldi 0d
@@ -1879,6 +2095,50 @@ static const SciScriptPatcherEntry mothergoose256Signatures[] = {
// ===========================================================================
// Police Quest 1 VGA
+
+// When briefing is about to start in room 15, other officers will get into the room too.
+// When one of those officers gets into the way of ego, they will tell the player to sit down.
+// But control will be disabled right at that point. Ego may then go to his seat by himself,
+// or more often than not will just stand there. The player is unable to do anything.
+//
+// Sergeant Dooley will then enter the room. Tell the player to sit down 3 times and after
+// that it's game over.
+//
+// Because the Sergeant is telling the player to sit down, one has to assume that the player
+// is meant to still be in control. Which is why this script patch removes disabling of player control.
+//
+// The script also tries to make ego walk to the chair, but it fails because it gets stuck with other
+// actors. So I guess the safest way is to remove all of that and let the player do it manually.
+//
+// The responsible method seems to use a few hardcoded texts, which is why I have to assume that it's
+// not used anywhere else. I also checked all scripts and couldn't find any other calls to it.
+//
+// This of course also happens when using the original interpreter.
+//
+// Scripts work like this: manX::doit (script 134) triggers gab::changeState, which then triggers rm015::notify
+//
+// Applies to at least: English floppy
+// Responsible method: gab::changeState (script 152)
+// Fixes bug: #5865
+static const uint16 pq1vgaSignatureBriefingGettingStuck[] = {
+ 0x76, // push0
+ 0x45, 0x02, 0x00, // call export 2 of script 0 (disable control)
+ 0x38, SIG_ADDTOOFFSET(+2), // pushi notify
+ 0x76, // push0
+ 0x81, 0x02, // lag global[2] (get current room)
+ 0x4a, 0x04, // send 04
+ SIG_MAGICDWORD,
+ 0x8b, 0x02, // lsl local[2]
+ 0x35, 0x01, // ldi 01
+ 0x02, // add
+ SIG_END
+};
+
+static const uint16 pq1vgaPatchBriefingGettingStuck[] = {
+ 0x33, 0x0a, // jmp to lsl local[2], skip over export 2 and ::notify
+ PATCH_END // rm015::notify would try to make ego walk to the chair
+};
+
// When at the police station, you can put or get your gun from your locker.
// The script, that handles this, is buggy. It disposes the gun as soon as
// you click, but then waits 2 seconds before it also closes the locker.
@@ -1966,10 +2226,11 @@ static const uint16 pq1vgaPatchMapSaveRestoreBug[] = {
PATCH_END
};
-// script, description, signature patch
+// script, description, signature patch
static const SciScriptPatcherEntry pq1vgaSignatures[] = {
- { true, 341, "put gun in locker bug", 1, pq1vgaSignaturePutGunInLockerBug, pq1vgaPatchPutGunInLockerBug },
- { true, 500, "map save/restore bug", 2, pq1vgaSignatureMapSaveRestoreBug, pq1vgaPatchMapSaveRestoreBug },
+ { true, 152, "getting stuck while briefing is about to start", 1, pq1vgaSignatureBriefingGettingStuck, pq1vgaPatchBriefingGettingStuck },
+ { true, 341, "put gun in locker bug", 1, pq1vgaSignaturePutGunInLockerBug, pq1vgaPatchPutGunInLockerBug },
+ { true, 500, "map save/restore bug", 2, pq1vgaSignatureMapSaveRestoreBug, pq1vgaPatchMapSaveRestoreBug },
SCI_SIGNATUREENTRY_TERMINATOR
};
@@ -2673,14 +2934,75 @@ static const uint16 qfg3PatchChiefPriority[] = {
PATCH_END
};
+// Partly WORKAROUND:
+// During combat, the game is not properly throttled. That's because the game uses
+// an inner loop for combat and does not iterate through the main loop.
+// It also doesn't call kGameIsRestarting. This may get fixed properly at some point
+// by rewriting the speed throttler.
+//
+// Additionally Sierra set the cycle speed of the hero to 0. Which explains
+// why the actions of the hero are so incredibly fast. This issue also happened
+// in the original interpreter, when the computer was too powerful.
+//
+// Applies to at least: English, French, German, Italian, Spanish PC floppy
+// Responsible method: combatControls::dispatchEvent (script 550) + WarriorObj in heap
+// Fixes bug #6247
+static const uint16 qfg3SignatureCombatSpeedThrottling1[] = {
+ 0x31, 0x0d, // bnt [skip code]
+ SIG_MAGICDWORD,
+ 0x89, 0xd2, // lsg global[D2h]
+ 0x35, 0x00, // ldi 0
+ 0x1e, // gt?
+ 0x31, 0x06, // bnt [skip code]
+ 0xe1, 0xd2, // -ag global[D2h] (jump skips over this)
+ 0x81, 0x58, // lag global[58h]
+ 0xa3, 0x01, // sal local[01]
+ SIG_END
+};
+
+static const uint16 qfg3PatchCombatSpeedThrottling1[] = {
+ 0x80, 0xd2, // lsg global[D2h]
+ 0x14, // or
+ 0x31, 0x06, // bnt [skip code] - saves 4 bytes
+ 0xe1, 0xd2, // -ag global[D2h]
+ 0x81, 0x58, // lag global[58h]
+ 0xa3, 0x01, // sal local[01] (jump skips over this)
+ // our code
+ 0x76, // push0
+ 0x43, 0x2c, 0x00, // callk GameIsRestarting <-- add this so that our speed throttler is triggered
+ PATCH_END
+};
+
+static const uint16 qfg3SignatureCombatSpeedThrottling2[] = {
+ SIG_MAGICDWORD,
+ SIG_UINT16(12), // priority 12
+ SIG_UINT16(0), // underbits 0
+ SIG_UINT16(0x4010), // signal 4010h
+ SIG_ADDTOOFFSET(+18),
+ SIG_UINT16(0), // scaleSignal 0
+ SIG_UINT16(128), // scaleX
+ SIG_UINT16(128), // scaleY
+ SIG_UINT16(128), // maxScale
+ SIG_UINT16(0), // cycleSpeed
+ SIG_END
+};
+
+static const uint16 qfg3PatchCombatSpeedThrottling2[] = {
+ PATCH_ADDTOOFFSET(+32),
+ PATCH_UINT16(5), // set cycleSpeed to 5
+ PATCH_END
+};
+
// script, description, signature patch
static const SciScriptPatcherEntry qfg3Signatures[] = {
- { true, 944, "import dialog continuous calls", 1, qfg3SignatureImportDialog, qfg3PatchImportDialog },
- { true, 440, "dialog crash when asking about Woo", 1, qfg3SignatureWooDialog, qfg3PatchWooDialog },
- { true, 440, "dialog crash when asking about Woo", 1, qfg3SignatureWooDialogAlt, qfg3PatchWooDialogAlt },
- { true, 52, "export character save bug", 2, qfg3SignatureExportChar, qfg3PatchExportChar },
- { true, 54, "import character from QfG1 bug", 1, qfg3SignatureImportQfG1Char, qfg3PatchImportQfG1Char },
- { true, 640, "chief in hut priority fix", 1, qfg3SignatureChiefPriority, qfg3PatchChiefPriority },
+ { true, 944, "import dialog continuous calls", 1, qfg3SignatureImportDialog, qfg3PatchImportDialog },
+ { true, 440, "dialog crash when asking about Woo", 1, qfg3SignatureWooDialog, qfg3PatchWooDialog },
+ { true, 440, "dialog crash when asking about Woo", 1, qfg3SignatureWooDialogAlt, qfg3PatchWooDialogAlt },
+ { true, 52, "export character save bug", 2, qfg3SignatureExportChar, qfg3PatchExportChar },
+ { true, 54, "import character from QfG1 bug", 1, qfg3SignatureImportQfG1Char, qfg3PatchImportQfG1Char },
+ { true, 640, "chief in hut priority fix", 1, qfg3SignatureChiefPriority, qfg3PatchChiefPriority },
+ { true, 550, "combat speed throttling script", 1, qfg3SignatureCombatSpeedThrottling1, qfg3PatchCombatSpeedThrottling1 },
+ { true, 550, "combat speed throttling heap", 1, qfg3SignatureCombatSpeedThrottling2, qfg3PatchCombatSpeedThrottling2 },
SCI_SIGNATUREENTRY_TERMINATOR
};
@@ -2836,6 +3158,66 @@ static const uint16 sq4CdPatchGetPointsForChangingBackClothes[] = {
PATCH_END
};
+
+// For Space Quest 4 CD, Sierra added a pick up animation for Roger, when he picks up the rope.
+//
+// When the player is detected by the zombie right at the start of the game, while picking up the rope,
+// scripts bomb out. This also happens, when using the original interpreter.
+//
+// This is caused by code, that's supposed to make Roger face the arriving drone.
+// We fix it, by checking if ego::cycler is actually set before calling that code.
+//
+// Applies to at least: English PC CD
+// Responsible method: droidShoots::changeState(3)
+// Fixes bug: #6076
+static const uint16 sq4CdSignatureGettingShotWhileGettingRope[] = {
+ 0x35, 0x02, // ldi 02
+ 0x65, 0x1a, // aTop cycles
+ 0x32, SIG_UINT16(0x02fa), // jmp [end]
+ SIG_MAGICDWORD,
+ 0x3c, // dup
+ 0x35, 0x02, // ldi 02
+ 0x1a, // eq?
+ 0x31, 0x0b, // bnt [state 3 check]
+ 0x76, // push0
+ 0x45, 0x02, 0x00, // call export 2 of script 0 -> disable controls
+ 0x35, 0x02, // ldi 02
+ 0x65, 0x1a, // aTop cycles
+ 0x32, SIG_UINT16(0x02e9), // jmp [end]
+ 0x3c, // dup
+ 0x35, 0x03, // ldi 03
+ 0x1a, // eq?
+ 0x31, 0x1e, // bnt [state 4 check]
+ 0x76, // push0
+ 0x45, 0x02, 0x00, // call export 2 of script 0 -> disable controls again??
+ 0x7a, // push2
+ 0x89, 0x00, // lsg global[0]
+ 0x72, SIG_UINT16(0x0242), // lofsa deathDroid
+ 0x36, // push
+ 0x45, 0x0d, 0x04, // call export 13 of script 0 -> set heading of ego to face droid
+ SIG_END
+};
+
+static const uint16 sq4CdPatchGettingShotWhileGettingRope[] = {
+ PATCH_ADDTOOFFSET(+11),
+ // this makes state 2 only do the 2 cycles wait, controls should always be disabled already at this point
+ 0x2f, 0xf3, // bt [previous state aTop cycles code]
+ // Now we check for state 3, this change saves us 11 bytes
+ 0x3c, // dup
+ 0x35, 0x03, // ldi 03
+ 0x1a, // eq?
+ 0x31, 0x29, // bnt [state 4 check]
+ // new state 3 code
+ 0x76, // push0
+ 0x45, 0x02, 0x00, // call export 2 of script 0 (disable controls, actually not needed)
+ 0x38, PATCH_SELECTOR16(cycler), // pushi cycler
+ 0x76, // push0
+ 0x81, 0x00, // lag global[0]
+ 0x4a, 0x04, // send 04 (get ego::cycler)
+ 0x30, PATCH_UINT16(10), // bnt [jump over heading call]
+ PATCH_END
+};
+
// The scripts in SQ4CD support simultaneous playing of speech and subtitles,
// but this was not available as an option. The following two patches enable
// this functionality in the game's GUI options dialog.
@@ -2932,6 +3314,7 @@ static const SciScriptPatcherEntry sq4Signatures[] = {
{ true, 700, "Floppy: throw stuff at sequel police bug", 1, sq4FloppySignatureThrowStuffAtSequelPoliceBug, sq4FloppyPatchThrowStuffAtSequelPoliceBug },
{ true, 45, "CD: walk in from below for room 45 fix", 1, sq4CdSignatureWalkInFromBelowRoom45, sq4CdPatchWalkInFromBelowRoom45 },
{ true, 396, "CD: get points for changing back clothes fix",1, sq4CdSignatureGetPointsForChangingBackClothes, sq4CdPatchGetPointsForChangingBackClothes },
+ { true, 701, "CD: getting shot, while getting rope", 1, sq4CdSignatureGettingShotWhileGettingRope, sq4CdPatchGettingShotWhileGettingRope },
{ true, 0, "CD: Babble icon speech and subtitles fix", 1, sq4CdSignatureBabbleIcon, sq4CdPatchBabbleIcon },
{ true, 818, "CD: Speech and subtitles option", 1, sq4CdSignatureTextOptions, sq4CdPatchTextOptions },
{ true, 818, "CD: Speech and subtitles option button", 1, sq4CdSignatureTextOptionsButton, sq4CdPatchTextOptionsButton },
@@ -3374,20 +3757,20 @@ bool ScriptPatcher::verifySignature(uint32 byteOffset, const uint16 *signatureDa
}
// will return -1 if no match was found, otherwise an offset to the start of the signature match
-int32 ScriptPatcher::findSignature(const SciScriptPatcherEntry *patchEntry, SciScriptPatcherRuntimeEntry *runtimeEntry, const byte *scriptData, const uint32 scriptSize) {
+int32 ScriptPatcher::findSignature(uint32 magicDWord, int magicOffset, const uint16 *signatureData, const char *patchDescription, const byte *scriptData, const uint32 scriptSize) {
if (scriptSize < 4) // we need to find a DWORD, so less than 4 bytes is not okay
return -1;
- const uint32 magicDWord = runtimeEntry->magicDWord; // is platform-specific BE/LE form, so that the later match will work
+ // magicDWord is in platform-specific BE/LE form, so that the later match will work, this was done for performance
const uint32 searchLimit = scriptSize - 3;
uint32 DWordOffset = 0;
// first search for the magic DWORD
while (DWordOffset < searchLimit) {
if (magicDWord == READ_UINT32(scriptData + DWordOffset)) {
// magic DWORD found, check if actual signature matches
- uint32 offset = DWordOffset + runtimeEntry->magicOffset;
+ uint32 offset = DWordOffset + magicOffset;
- if (verifySignature(offset, patchEntry->signatureData, patchEntry->description, scriptData, scriptSize))
+ if (verifySignature(offset, signatureData, patchDescription, scriptData, scriptSize))
return offset;
}
DWordOffset++;
@@ -3396,22 +3779,146 @@ int32 ScriptPatcher::findSignature(const SciScriptPatcherEntry *patchEntry, SciS
return -1;
}
-// This method calculates the magic DWORD for each entry in the signature table
-// and it also initializes the selector table for selectors used in the signatures/patches of the current game
-void ScriptPatcher::initSignature(const SciScriptPatcherEntry *patchTable) {
- const SciScriptPatcherEntry *curEntry = patchTable;
- SciScriptPatcherRuntimeEntry *curRuntimeEntry;
+int32 ScriptPatcher::findSignature(const SciScriptPatcherEntry *patchEntry, const SciScriptPatcherRuntimeEntry *runtimeEntry, const byte *scriptData, const uint32 scriptSize) {
+ return findSignature(runtimeEntry->magicDWord, runtimeEntry->magicOffset, patchEntry->signatureData, patchEntry->description, scriptData, scriptSize);
+}
+
+// Attention: Magic DWord is returns using platform specific byte order. This is done on purpose for performance.
+void ScriptPatcher::calculateMagicDWordAndVerify(const char *signatureDescription, const uint16 *signatureData, bool magicDWordIncluded, uint32 &calculatedMagicDWord, int &calculatedMagicDWordOffset) {
Selector curSelector = -1;
- int step;
int magicOffset;
byte magicDWord[4];
int magicDWordLeft = 0;
- const uint16 *curData;
uint16 curWord;
uint16 curCommand;
uint32 curValue;
byte byte1 = 0;
byte byte2 = 0;
+
+ memset(magicDWord, 0, sizeof(magicDWord));
+
+ curWord = *signatureData;
+ magicOffset = 0;
+ while (curWord != SIG_END) {
+ curCommand = curWord & SIG_COMMANDMASK;
+ curValue = curWord & SIG_VALUEMASK;
+ switch (curCommand) {
+ case SIG_MAGICDWORD: {
+ if (magicDWordIncluded) {
+ if ((calculatedMagicDWord) || (magicDWordLeft))
+ error("Script-Patcher: Magic-DWORD specified multiple times in signature\nFaulty patch: '%s'", signatureDescription);
+ magicDWordLeft = 4;
+ calculatedMagicDWordOffset = magicOffset;
+ } else {
+ error("Script-Patcher: Magic-DWORD sequence found in patch data\nFaulty patch: '%s'", signatureDescription);
+ }
+ break;
+ }
+ case SIG_CODE_ADDTOOFFSET: {
+ magicOffset -= curValue;
+ if (magicDWordLeft)
+ error("Script-Patcher: Magic-DWORD contains AddToOffset command\nFaulty patch: '%s'", signatureDescription);
+ break;
+ }
+ case SIG_CODE_UINT16:
+ case SIG_CODE_SELECTOR16: {
+ // UINT16 or 1
+ switch (curCommand) {
+ case SIG_CODE_UINT16: {
+ signatureData++; curWord = *signatureData;
+ if (curWord & SIG_COMMANDMASK)
+ error("Script-Patcher: signature entry inconsistent\nFaulty patch: '%s'", signatureDescription);
+ if (!_isMacSci11) {
+ byte1 = curValue;
+ byte2 = curWord & SIG_BYTEMASK;
+ } else {
+ byte1 = curWord & SIG_BYTEMASK;
+ byte2 = curValue;
+ }
+ break;
+ }
+ case SIG_CODE_SELECTOR16: {
+ curSelector = _selectorIdTable[curValue];
+ if (curSelector == -1) {
+ curSelector = g_sci->getKernel()->findSelector(selectorNameTable[curValue]);
+ _selectorIdTable[curValue] = curSelector;
+ }
+ if (!_isMacSci11) {
+ byte1 = curSelector & 0x00FF;
+ byte2 = curSelector >> 8;
+ } else {
+ byte1 = curSelector >> 8;
+ byte2 = curSelector & 0x00FF;
+ }
+ break;
+ }
+ }
+ magicOffset -= 2;
+ if (magicDWordLeft) {
+ // Remember current word for Magic DWORD
+ magicDWord[4 - magicDWordLeft] = byte1;
+ magicDWordLeft--;
+ if (magicDWordLeft) {
+ magicDWord[4 - magicDWordLeft] = byte2;
+ magicDWordLeft--;
+ }
+ if (!magicDWordLeft) {
+ // Magic DWORD is now known, convert to platform specific byte order
+ calculatedMagicDWord = READ_LE_UINT32(magicDWord);
+ }
+ }
+ break;
+ }
+ case SIG_CODE_BYTE:
+ case SIG_CODE_SELECTOR8: {
+ if (curCommand == SIG_CODE_SELECTOR8) {
+ curSelector = _selectorIdTable[curValue];
+ if (curSelector == -1) {
+ curSelector = g_sci->getKernel()->findSelector(selectorNameTable[curValue]);
+ _selectorIdTable[curValue] = curSelector;
+ if (curSelector != -1) {
+ if (curSelector & 0xFF00)
+ error("Script-Patcher: 8 bit selector required, game uses 16 bit selector\nFaulty patch: '%s'", signatureDescription);
+ }
+ }
+ curValue = curSelector;
+ }
+ magicOffset--;
+ if (magicDWordLeft) {
+ // Remember current byte for Magic DWORD
+ magicDWord[4 - magicDWordLeft] = (byte)curValue;
+ magicDWordLeft--;
+ if (!magicDWordLeft) {
+ calculatedMagicDWord = READ_LE_UINT32(magicDWord);
+ }
+ }
+ break;
+ }
+ case PATCH_CODE_GETORIGINALBYTEADJUST: {
+ signatureData++; // skip over extra uint16
+ break;
+ }
+ default:
+ break;
+ }
+ signatureData++;
+ curWord = *signatureData;
+ }
+
+ if (magicDWordLeft)
+ error("Script-Patcher: Magic-DWORD beyond End-Of-Signature\nFaulty patch: '%s'", signatureDescription);
+ if (magicDWordIncluded) {
+ if (!calculatedMagicDWord) {
+ error("Script-Patcher: Magic-DWORD not specified in signature\nFaulty patch: '%s'", signatureDescription);
+ }
+ }
+}
+
+// This method calculates the magic DWORD for each entry in the signature table
+// and it also initializes the selector table for selectors used in the signatures/patches of the current game
+void ScriptPatcher::initSignature(const SciScriptPatcherEntry *patchTable) {
+ const SciScriptPatcherEntry *curEntry = patchTable;
+ SciScriptPatcherRuntimeEntry *curRuntimeEntry;
int patchEntryCount = 0;
// Count entries and allocate runtime data
@@ -3425,120 +3932,14 @@ void ScriptPatcher::initSignature(const SciScriptPatcherEntry *patchTable) {
curRuntimeEntry = _runtimeTable;
while (curEntry->signatureData) {
// process signature
- memset(magicDWord, 0, sizeof(magicDWord));
-
curRuntimeEntry->active = curEntry->defaultActive;
curRuntimeEntry->magicDWord = 0;
curRuntimeEntry->magicOffset = 0;
- for (step = 0; step < 2; step++) {
- switch (step) {
- case 0: curData = curEntry->signatureData; break;
- case 1: curData = curEntry->patchData; break;
- }
-
- curWord = *curData;
- magicOffset = 0;
- while (curWord != SIG_END) {
- curCommand = curWord & SIG_COMMANDMASK;
- curValue = curWord & SIG_VALUEMASK;
- switch (curCommand) {
- case SIG_MAGICDWORD: {
- if (step == 0) {
- if ((curRuntimeEntry->magicDWord) || (magicDWordLeft))
- error("Script-Patcher: Magic-DWORD specified multiple times in signature\nFaulty patch: '%s'", curEntry->description);
- magicDWordLeft = 4;
- curRuntimeEntry->magicOffset = magicOffset;
- }
- break;
- }
- case SIG_CODE_ADDTOOFFSET: {
- magicOffset -= curValue;
- if (magicDWordLeft)
- error("Script-Patcher: Magic-DWORD contains AddToOffset command\nFaulty patch: '%s'", curEntry->description);
- break;
- }
- case SIG_CODE_UINT16:
- case SIG_CODE_SELECTOR16: {
- // UINT16 or 1
- switch (curCommand) {
- case SIG_CODE_UINT16: {
- curData++; curWord = *curData;
- if (curWord & SIG_COMMANDMASK)
- error("Script-Patcher: signature entry inconsistent\nFaulty patch: '%s'", curEntry->description);
- if (!_isMacSci11) {
- byte1 = curValue;
- byte2 = curWord & SIG_BYTEMASK;
- } else {
- byte1 = curWord & SIG_BYTEMASK;
- byte2 = curValue;
- }
- break;
- }
- case SIG_CODE_SELECTOR16: {
- curSelector = _selectorIdTable[curValue];
- if (curSelector == -1) {
- curSelector = g_sci->getKernel()->findSelector(selectorNameTable[curValue]);
- _selectorIdTable[curValue] = curSelector;
- }
- if (!_isMacSci11) {
- byte1 = curSelector & 0x00FF;
- byte2 = curSelector >> 8;
- } else {
- byte1 = curSelector >> 8;
- byte2 = curSelector & 0x00FF;
- }
- break;
- }
- }
- magicOffset -= 2;
- if (magicDWordLeft) {
- // Remember current word for Magic DWORD
- magicDWord[4 - magicDWordLeft] = byte1;
- magicDWordLeft--;
- if (magicDWordLeft) {
- magicDWord[4 - magicDWordLeft] = byte2;
- magicDWordLeft--;
- }
- if (!magicDWordLeft) {
- curRuntimeEntry->magicDWord = READ_LE_UINT32(magicDWord);
- }
- }
- break;
- }
- case SIG_CODE_BYTE:
- case SIG_CODE_SELECTOR8: {
- if (curCommand == SIG_CODE_SELECTOR8) {
- curSelector = _selectorIdTable[curValue];
- if (curSelector == -1) {
- curSelector = g_sci->getKernel()->findSelector(selectorNameTable[curValue]);
- _selectorIdTable[curValue] = curSelector;
- if (curSelector != -1) {
- if (curSelector & 0xFF00)
- error("Script-Patcher: 8 bit selector required, game uses 16 bit selector\nFaulty patch: '%s'", curEntry->description);
- }
- }
- curValue = curSelector;
- }
- magicOffset--;
- if (magicDWordLeft) {
- // Remember current byte for Magic DWORD
- magicDWord[4 - magicDWordLeft] = (byte)curValue;
- magicDWordLeft--;
- if (!magicDWordLeft) {
- curRuntimeEntry->magicDWord = READ_LE_UINT32(magicDWord);
- }
- }
- }
- }
- curData++;
- curWord = *curData;
- }
- }
- if (magicDWordLeft)
- error("Script-Patcher: Magic-DWORD beyond End-Of-Signature\nFaulty patch: '%s'", curEntry->description);
- if (!curRuntimeEntry->magicDWord)
- error("Script-Patcher: Magic-DWORD not specified in signature\nFaulty patch: '%s'", curEntry->description);
+ // We verify the signature data and remember the calculated magic DWord from the signature data
+ calculateMagicDWordAndVerify(curEntry->description, curEntry->signatureData, true, curRuntimeEntry->magicDWord, curRuntimeEntry->magicOffset);
+ // We verify the patch data
+ calculateMagicDWordAndVerify(curEntry->description, curEntry->patchData, false, curRuntimeEntry->magicDWord, curRuntimeEntry->magicOffset);
curEntry++; curRuntimeEntry++;
}
@@ -3596,6 +3997,9 @@ void ScriptPatcher::processScript(uint16 scriptNr, byte *scriptData, const uint3
case GID_KQ6:
signatureTable = kq6Signatures;
break;
+ case GID_LAURABOW:
+ signatureTable = laurabow1Signatures;
+ break;
case GID_LAURABOW2:
signatureTable = laurabow2Signatures;
break;
diff --git a/engines/sci/engine/script_patches.h b/engines/sci/engine/script_patches.h
index d15fce321b..645e0946b3 100644
--- a/engines/sci/engine/script_patches.h
+++ b/engines/sci/engine/script_patches.h
@@ -90,13 +90,32 @@ public:
ScriptPatcher();
~ScriptPatcher();
+ // Calculates the magic DWord for fast search and verifies signature/patch data
+ // Returns the magic DWord in platform-specific byte-order. This is done on purpose for performance.
+ void calculateMagicDWordAndVerify(const char *signatureDescription, const uint16 *signatureData, bool magicDWordIncluded, uint32 &calculatedMagicDWord, int &calculatedMagicDWordOffset);
+
+ // Called when a script is loaded to check for signature matches and apply patches in such cases
void processScript(uint16 scriptNr, byte *scriptData, const uint32 scriptSize);
+
+ // Verifies, if a given signature matches the given script data (pointed to by additional byte offset)
bool verifySignature(uint32 byteOffset, const uint16 *signatureData, const char *signatureDescription, const byte *scriptData, const uint32 scriptSize);
+ // searches for a given signature inside script data
+ // returns -1 in case it was not found or an offset to the matching data
+ int32 findSignature(uint32 magicDWord, int magicOffset, const uint16 *signatureData, const char *patchDescription, const byte *scriptData, const uint32 scriptSize);
+
private:
+ // Initializes a patch table and creates run time information for it (for enabling/disabling), also calculates magic DWORD)
void initSignature(const SciScriptPatcherEntry *patchTable);
+
+ // Enables a patch inside the patch table (used for optional patches like CD+Text support for KQ6 & LB2)
void enablePatch(const SciScriptPatcherEntry *patchTable, const char *searchDescription);
- int32 findSignature(const SciScriptPatcherEntry *patchEntry, SciScriptPatcherRuntimeEntry *runtimeEntry, const byte *scriptData, const uint32 scriptSize);
+
+ // Searches for a given signature entry inside script data
+ // returns -1 in case it was not found or an offset to the matching data
+ int32 findSignature(const SciScriptPatcherEntry *patchEntry, const SciScriptPatcherRuntimeEntry *runtimeEntry, const byte *scriptData, const uint32 scriptSize);
+
+ // Applies a patch to a given script + offset (overwrites parts)
void applyPatch(const SciScriptPatcherEntry *patchEntry, byte *scriptData, const uint32 scriptSize, int32 signatureOffset);
Selector *_selectorIdTable;
diff --git a/engines/sci/engine/scriptdebug.cpp b/engines/sci/engine/scriptdebug.cpp
index f0157a6569..7d70f30d55 100644
--- a/engines/sci/engine/scriptdebug.cpp
+++ b/engines/sci/engine/scriptdebug.cpp
@@ -499,7 +499,7 @@ void Kernel::dumpScriptClass(char *data, int seeker, int objsize) {
void Kernel::dissectScript(int scriptNumber, Vocabulary *vocab) {
int objectctr[11] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
- unsigned int _seeker = 0;
+ uint32 _seeker = 0;
Resource *script = _resMan->findResource(ResourceId(kResourceTypeScript, scriptNumber), 0);
if (!script) {
@@ -510,7 +510,7 @@ void Kernel::dissectScript(int scriptNumber, Vocabulary *vocab) {
while (_seeker < script->size) {
int objType = (int16)READ_SCI11ENDIAN_UINT16(script->data + _seeker);
int objsize;
- unsigned int seeker = _seeker + 4;
+ uint32 seeker = _seeker + 4;
if (!objType) {
debugN("End of script object (#0) encountered.\n");
diff --git a/engines/sci/engine/seg_manager.cpp b/engines/sci/engine/seg_manager.cpp
index 23b1fdae23..8090b1861d 100644
--- a/engines/sci/engine/seg_manager.cpp
+++ b/engines/sci/engine/seg_manager.cpp
@@ -837,10 +837,13 @@ byte *SegManager::allocDynmem(int size, const char *descr, reg_t *addr) {
d._size = size;
- if (size == 0)
+ // Original SCI only zeroed out heap memory on initialize
+ // They didn't do it again for every allocation
+ if (size) {
+ d._buf = (byte *)calloc(size, 1);
+ } else {
d._buf = NULL;
- else
- d._buf = (byte *)malloc(size);
+ }
d._description = descr;
diff --git a/engines/sci/engine/segment.h b/engines/sci/engine/segment.h
index de7f60ac16..2699bc2e5b 100644
--- a/engines/sci/engine/segment.h
+++ b/engines/sci/engine/segment.h
@@ -203,7 +203,7 @@ struct List {
struct Hunk {
void *mem;
- unsigned int size;
+ uint32 size;
const char *type;
};
diff --git a/engines/sci/engine/selector.cpp b/engines/sci/engine/selector.cpp
index 910f1f885f..ac621f58ae 100644
--- a/engines/sci/engine/selector.cpp
+++ b/engines/sci/engine/selector.cpp
@@ -179,6 +179,8 @@ void Kernel::mapSelectors() {
FIND_SELECTOR(fore);
FIND_SELECTOR(back);
FIND_SELECTOR(skip);
+ FIND_SELECTOR(borderColor);
+ FIND_SELECTOR(width);
FIND_SELECTOR(fixPriority);
FIND_SELECTOR(mirrored);
FIND_SELECTOR(visible);
@@ -187,6 +189,17 @@ void Kernel::mapSelectors() {
FIND_SELECTOR(inLeft);
FIND_SELECTOR(inBottom);
FIND_SELECTOR(inRight);
+ FIND_SELECTOR(textTop);
+ FIND_SELECTOR(textLeft);
+ FIND_SELECTOR(textBottom);
+ FIND_SELECTOR(textRight);
+ FIND_SELECTOR(title);
+ FIND_SELECTOR(titleFont);
+ FIND_SELECTOR(titleFore);
+ FIND_SELECTOR(titleBack);
+ FIND_SELECTOR(magnifier);
+ FIND_SELECTOR(frameOut);
+ FIND_SELECTOR(casts);
#endif
}
@@ -199,6 +212,16 @@ reg_t readSelector(SegManager *segMan, reg_t object, Selector selectorId) {
return *address.getPointer(segMan);
}
+#ifdef ENABLE_SCI32
+void updateInfoFlagViewVisible(Object *obj, int index) {
+ // TODO: Make this correct for all SCI versions
+ // Selectors 26 through 44 are selectors for View script objects in SQ6
+ if (index >= 26 && index <= 44 && getSciVersion() >= SCI_VERSION_2) {
+ obj->setInfoSelectorFlag(kInfoFlagViewVisible);
+ }
+}
+#endif
+
void writeSelector(SegManager *segMan, reg_t object, Selector selectorId, reg_t value) {
ObjVarRef address;
@@ -211,8 +234,12 @@ void writeSelector(SegManager *segMan, reg_t object, Selector selectorId, reg_t
if (lookupSelector(segMan, object, selectorId, &address, NULL) != kSelectorVariable)
error("Selector '%s' of object at %04x:%04x could not be"
" written to", g_sci->getKernel()->getSelectorName(selectorId).c_str(), PRINT_REG(object));
- else
+ else {
*address.getPointer(segMan) = value;
+#ifdef ENABLE_SCI32
+ updateInfoFlagViewVisible(segMan->getObject(object), selectorId);
+#endif
+ }
}
void invokeSelector(EngineState *s, reg_t object, int selectorId,
diff --git a/engines/sci/engine/selector.h b/engines/sci/engine/selector.h
index b3dd393708..f2d06d1cf4 100644
--- a/engines/sci/engine/selector.h
+++ b/engines/sci/engine/selector.h
@@ -146,6 +146,8 @@ struct SelectorCache {
Selector back;
Selector skip;
Selector dimmed;
+ Selector borderColor;
+ Selector width;
Selector fixPriority;
Selector mirrored;
@@ -153,6 +155,12 @@ struct SelectorCache {
Selector useInsetRect;
Selector inTop, inLeft, inBottom, inRight;
+ Selector textTop, textLeft, textBottom, textRight;
+ Selector title, titleFont, titleFore, titleBack;
+
+ Selector magnifier;
+ Selector frameOut;
+ Selector casts; // needed for sync'ing screen items/planes with scripts, when our save/restore code is patched in (see GfxFrameout::syncWithScripts)
#endif
};
@@ -191,6 +199,16 @@ void writeSelector(SegManager *segMan, reg_t object, Selector selectorId, reg_t
void invokeSelector(EngineState *s, reg_t object, int selectorId,
int k_argc, StackPtr k_argp, int argc = 0, const reg_t *argv = 0);
+#ifdef ENABLE_SCI32
+/**
+ * SCI32 set kInfoFlagViewVisible in the -info- selector if a certain
+ * range of properties was written to.
+ * This function checks if index is in the right range, and sets the flag
+ * on obj.-info- if it is.
+ */
+void updateInfoFlagViewVisible(Object *obj, int index);
+#endif
+
} // End of namespace Sci
#endif // SCI_ENGINE_KERNEL_H
diff --git a/engines/sci/engine/state.cpp b/engines/sci/engine/state.cpp
index d53e6b48c8..fda78317b5 100644
--- a/engines/sci/engine/state.cpp
+++ b/engines/sci/engine/state.cpp
@@ -95,6 +95,7 @@ void EngineState::reset(bool isRestoring) {
// reset delayed restore game functionality
_delayedRestoreGame = false;
_delayedRestoreGameId = 0;
+ _delayedRestoreFromLauncher = false;
executionStackBase = 0;
_executionStackPosChanged = false;
diff --git a/engines/sci/engine/state.h b/engines/sci/engine/state.h
index 0f04e32fe5..cf9a753f5c 100644
--- a/engines/sci/engine/state.h
+++ b/engines/sci/engine/state.h
@@ -138,10 +138,12 @@ public:
// see detection.cpp / SciEngine::loadGameState()
bool _delayedRestoreGame; // boolean, that triggers delayed restore (triggered by ScummVM menu)
int _delayedRestoreGameId; // the saved game id, that it supposed to get restored (triggered by ScummVM menu)
+ bool _delayedRestoreFromLauncher; // is set, when the the delayed restore game was triggered from launcher
uint _chosenQfGImportItem; // Remembers the item selected in QfG import rooms
bool _cursorWorkaroundActive; // Refer to GfxCursor::setPosition()
+ int16 _cursorWorkaroundPosCount; // When the cursor is reported to be at the previously set coordinate, we won't disable the workaround unless it happened for this many times
Common::Point _cursorWorkaroundPoint;
Common::Rect _cursorWorkaroundRect;
diff --git a/engines/sci/engine/vm.cpp b/engines/sci/engine/vm.cpp
index 3729fc5236..64e6c045db 100644
--- a/engines/sci/engine/vm.cpp
+++ b/engines/sci/engine/vm.cpp
@@ -258,6 +258,10 @@ static void _exec_varselectors(EngineState *s) {
if (xs.argc) { // write?
*var = xs.variables_argp[1];
+#ifdef ENABLE_SCI32
+ updateInfoFlagViewVisible(s->_segMan->getObject(xs.addr.varp.obj), xs.addr.varp.varindex);
+#endif
+
} else // No, read
s->r_acc = *var;
}
@@ -1095,6 +1099,9 @@ void run_vm(EngineState *s) {
case op_aTop: // 0x32 (50)
// Accumulator To Property
validate_property(s, obj, opparams[0]) = s->r_acc;
+#ifdef ENABLE_SCI32
+ updateInfoFlagViewVisible(obj, opparams[0]>>1);
+#endif
break;
case op_pTos: // 0x33 (51)
@@ -1105,6 +1112,9 @@ void run_vm(EngineState *s) {
case op_sTop: // 0x34 (52)
// Stack To Property
validate_property(s, obj, opparams[0]) = POP32();
+#ifdef ENABLE_SCI32
+ updateInfoFlagViewVisible(obj, opparams[0]>>1);
+#endif
break;
case op_ipToa: // 0x35 (53)
@@ -1119,7 +1129,9 @@ void run_vm(EngineState *s) {
opProperty += 1;
else
opProperty -= 1;
-
+#ifdef ENABLE_SCI32
+ updateInfoFlagViewVisible(obj, opparams[0]>>1);
+#endif
if (opcode == op_ipToa || opcode == op_dpToa)
s->r_acc = opProperty;
else
diff --git a/engines/sci/engine/vm_types.cpp b/engines/sci/engine/vm_types.cpp
index cf008c45e1..d74e2b194c 100644
--- a/engines/sci/engine/vm_types.cpp
+++ b/engines/sci/engine/vm_types.cpp
@@ -210,12 +210,30 @@ reg_t reg_t::operator^(const reg_t right) const {
return lookForWorkaround(right, "bitwise XOR");
}
+#ifdef ENABLE_SCI32
+reg_t reg_t::operator&(int16 right) const {
+ return *this & make_reg(0, right);
+}
+
+reg_t reg_t::operator|(int16 right) const {
+ return *this | make_reg(0, right);
+}
+
+reg_t reg_t::operator^(int16 right) const {
+ return *this ^ make_reg(0, right);
+}
+#endif
+
int reg_t::cmp(const reg_t right, bool treatAsUnsigned) const {
if (getSegment() == right.getSegment()) { // can compare things in the same segment
if (treatAsUnsigned || !isNumber())
return toUint16() - right.toUint16();
else
return toSint16() - right.toSint16();
+#ifdef ENABLE_SCI32
+ } else if (getSciVersion() >= SCI_VERSION_2) {
+ return sci32Comparison(right);
+#endif
} else if (pointerComparisonWithInteger(right)) {
return 1;
} else if (right.pointerComparisonWithInteger(*this)) {
@@ -224,6 +242,26 @@ int reg_t::cmp(const reg_t right, bool treatAsUnsigned) const {
return lookForWorkaround(right, "comparison").toSint16();
}
+#ifdef ENABLE_SCI32
+int reg_t::sci32Comparison(const reg_t right) const {
+ // In SCI32, MemIDs are normally indexes into the memory manager's handle
+ // list, but the engine reserves indexes at and above 20000 for objects
+ // that were created inside the engine (as opposed to inside the VM). The
+ // engine compares these as a tiebreaker for graphics objects that are at
+ // the same priority, and it is necessary to at least minimally handle
+ // this situation.
+ // This is obviously a bogus comparision, but then, this entire thing is
+ // bogus. For the moment, it just needs to be deterministic.
+ if (isNumber() && !right.isNumber()) {
+ return 1;
+ } else if (right.isNumber() && !isNumber()) {
+ return -1;
+ }
+
+ return getOffset() - right.getOffset();
+}
+#endif
+
bool reg_t::pointerComparisonWithInteger(const reg_t right) const {
// This function handles the case where a script tries to compare a pointer
// to a number. Normally, we would not want to allow that. However, SCI0 -
diff --git a/engines/sci/engine/vm_types.h b/engines/sci/engine/vm_types.h
index af78bd0b84..e60f52e85c 100644
--- a/engines/sci/engine/vm_types.h
+++ b/engines/sci/engine/vm_types.h
@@ -136,6 +136,19 @@ struct reg_t {
reg_t operator|(const reg_t right) const;
reg_t operator^(const reg_t right) const;
+#ifdef ENABLE_SCI32
+ reg_t operator&(int16 right) const;
+ reg_t operator|(int16 right) const;
+ reg_t operator^(int16 right) const;
+
+ void operator&=(const reg_t &right) { *this = *this & right; }
+ void operator|=(const reg_t &right) { *this = *this | right; }
+ void operator^=(const reg_t &right) { *this = *this ^ right; }
+ void operator&=(int16 right) { *this = *this & right; }
+ void operator|=(int16 right) { *this = *this | right; }
+ void operator^=(int16 right) { *this = *this ^ right; }
+#endif
+
private:
/**
* Compares two reg_t's.
@@ -147,6 +160,10 @@ private:
int cmp(const reg_t right, bool treatAsUnsigned) const;
reg_t lookForWorkaround(const reg_t right, const char *operation) const;
bool pointerComparisonWithInteger(const reg_t right) const;
+
+#ifdef ENABLE_SCI32
+ int sci32Comparison(const reg_t right) const;
+#endif
};
static inline reg_t make_reg(SegmentId segment, uint16 offset) {
diff --git a/engines/sci/engine/workarounds.cpp b/engines/sci/engine/workarounds.cpp
index aab32032f7..3832f4cf04 100644
--- a/engines/sci/engine/workarounds.cpp
+++ b/engines/sci/engine/workarounds.cpp
@@ -378,6 +378,7 @@ const SciWorkaroundEntry uninitializedReadWorkarounds[] = {
{ GID_SQ6, -1, 64950, -1, "Feature", "handleEvent", NULL, 0, { WORKAROUND_FAKE, 0 } }, // called when pressing "Start game" in the main menu, when entering the Orion's Belt bar (room 300), and perhaps other places
{ GID_SQ6, -1, 64964, 0, "DPath", "init", NULL, 1, { WORKAROUND_FAKE, 0 } }, // during the game
{ GID_TORIN, -1, 64017, 0, "oFlags", "clear", NULL, 0, { WORKAROUND_FAKE, 0 } }, // entering Torin's home in the French version
+ { GID_TORIN, 10000, 64029, 0, "oMessager", "nextMsg", NULL, 3, { WORKAROUND_FAKE, 0 } }, // start of chapter one
SCI_WORKAROUNDENTRY_TERMINATOR
};
@@ -630,7 +631,7 @@ const SciWorkaroundEntry kGraphUpdateBox_workarounds[] = {
// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround
const SciWorkaroundEntry kIsObject_workarounds[] = {
- { GID_GK1, 50, 999, 0, "List", "eachElementDo", NULL, 0, { WORKAROUND_FAKE, 0 } }, // GK1 demo, when asking Grace for messages it gets called with an invalid parameter (type "error") - bug #4950
+ { GID_GK1DEMO, 50, 999, 0, "List", "eachElementDo", NULL, 0, { WORKAROUND_FAKE, 0 } }, // GK1 demo, when asking Grace for messages it gets called with an invalid parameter (type "error") - bug #4950
{ GID_ISLANDBRAIN, -1, 999, 0, "List", "eachElementDo", NULL, 0, { WORKAROUND_FAKE, 0 } }, // when going to the game options, choosing "Info" and selecting anything from the list, gets called with an invalid parameter (type "error") - bug #4989
{ GID_QFG3, -1, 999, 0, "List", "eachElementDo", NULL, 0, { WORKAROUND_FAKE, 0 } }, // when asking for something, gets called with type error parameter
SCI_WORKAROUNDENTRY_TERMINATOR
@@ -656,6 +657,12 @@ const SciWorkaroundEntry kNewWindow_workarounds[] = {
};
// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround
+const SciWorkaroundEntry kPalVarySetPercent_workarounds[] = {
+ { GID_GK1, 370, 370, 0, "graceComeOut", "changeState", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // there's an extra parameter in GK1, when changing chapters. This extra parameter seems to be a bug or just unimplemented functionality, as there's no visible change from the original in the chapter change room
+ SCI_WORKAROUNDENTRY_TERMINATOR
+};
+
+// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround
const SciWorkaroundEntry kReadNumber_workarounds[] = {
{ GID_CNICK_LAURABOW,100, 101, 0, "dominoes.opt", "doit", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // When dominoes.opt is present, the game scripts call kReadNumber with an extra integer parameter - bug #6425
{ GID_HOYLE3, 100, 101, 0, "dominoes.opt", "doit", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // When dominoes.opt is present, the game scripts call kReadNumber with an extra integer parameter - bug #6425
@@ -664,7 +671,7 @@ const SciWorkaroundEntry kReadNumber_workarounds[] = {
// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround
const SciWorkaroundEntry kPaletteUnsetFlag_workarounds[] = {
- { GID_QFG4, 100, 100, 0, "doMovie", "changeState", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // after the Sierra logo, no flags are passed, thus the call is meaningless - bug #4947
+ { GID_QFG4DEMO, 100, 100, 0, "doMovie", "changeState", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // after the Sierra logo, no flags are passed, thus the call is meaningless - bug #4947
SCI_WORKAROUNDENTRY_TERMINATOR
};
diff --git a/engines/sci/engine/workarounds.h b/engines/sci/engine/workarounds.h
index 46059a175c..8f519a8c9c 100644
--- a/engines/sci/engine/workarounds.h
+++ b/engines/sci/engine/workarounds.h
@@ -89,6 +89,7 @@ extern const SciWorkaroundEntry kIsObject_workarounds[];
extern const SciWorkaroundEntry kMemory_workarounds[];
extern const SciWorkaroundEntry kMoveCursor_workarounds[];
extern const SciWorkaroundEntry kNewWindow_workarounds[];
+extern const SciWorkaroundEntry kPalVarySetPercent_workarounds[];
extern const SciWorkaroundEntry kReadNumber_workarounds[];
extern const SciWorkaroundEntry kPaletteUnsetFlag_workarounds[];
extern const SciWorkaroundEntry kSetCursor_workarounds[];
diff --git a/engines/sci/event.cpp b/engines/sci/event.cpp
index f8285c26f2..34f1618514 100644
--- a/engines/sci/event.cpp
+++ b/engines/sci/event.cpp
@@ -29,6 +29,9 @@
#include "sci/console.h"
#include "sci/engine/state.h"
#include "sci/engine/kernel.h"
+#ifdef ENABLE_SCI32
+#include "sci/graphics/frameout.h"
+#endif
#include "sci/graphics/screen.h"
namespace Sci {
@@ -44,7 +47,7 @@ static const ScancodeRow scancodeAltifyRows[] = {
{ 0x2c, "ZXCVBNM,./" }
};
-static const byte codepagemap_88591toDOS[0x80] = {
+static const byte codePageMap88591ToDOS[0x80] = {
'?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', // 0x8x
'?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', // 0x9x
'?', 0xad, 0x9b, 0x9c, '?', 0x9d, '?', 0x9e, '?', '?', 0xa6, 0xae, 0xaa, '?', '?', '?', // 0xAx
@@ -133,8 +136,13 @@ static int altify(int ch) {
}
SciEvent EventManager::getScummVMEvent() {
- SciEvent input = { SCI_EVENT_NONE, 0, 0, Common::Point(0, 0) };
- SciEvent noEvent = { SCI_EVENT_NONE, 0, 0, Common::Point(0, 0) };
+#ifdef ENABLE_SCI32
+ SciEvent input = { SCI_EVENT_NONE, 0, 0, Common::Point(), Common::Point() };
+ SciEvent noEvent = { SCI_EVENT_NONE, 0, 0, Common::Point(), Common::Point() };
+#else
+ SciEvent input = { SCI_EVENT_NONE, 0, 0, Common::Point() };
+ SciEvent noEvent = { SCI_EVENT_NONE, 0, 0, Common::Point() };
+#endif
Common::EventManager *em = g_system->getEventManager();
Common::Event ev;
@@ -155,7 +163,20 @@ SciEvent EventManager::getScummVMEvent() {
// via pollEvent.
// We also adjust the position based on the scaling of the screen.
Common::Point mousePos = em->getMousePos();
- g_sci->_gfxScreen->adjustBackUpscaledCoordinates(mousePos.y, mousePos.x);
+
+#if ENABLE_SCI32
+ if (getSciVersion() >= SCI_VERSION_2) {
+ Buffer &screen = g_sci->_gfxFrameout->getCurrentBuffer();
+
+ Common::Point mousePosSci = mousePos;
+ mulru(mousePosSci, Ratio(screen.scriptWidth, screen.screenWidth), Ratio(screen.scriptHeight, screen.screenHeight));
+ noEvent.mousePosSci = input.mousePosSci = mousePosSci;
+ } else {
+#endif
+ g_sci->_gfxScreen->adjustBackUpscaledCoordinates(mousePos.y, mousePos.x);
+#if ENABLE_SCI32
+ }
+#endif
noEvent.mousePos = input.mousePos = mousePos;
@@ -173,7 +194,7 @@ SciEvent EventManager::getScummVMEvent() {
return input;
}
- int scummVMKeyFlags;
+ int scummVMKeyFlags;
switch (ev.type) {
case Common::EVENT_KEYDOWN:
@@ -265,7 +286,7 @@ SciEvent EventManager::getScummVMEvent() {
return noEvent;
// Convert 8859-1 characters to DOS (cp850/437) for
// multilingual SCI01 games
- input.character = codepagemap_88591toDOS[input.character & 0x7f];
+ input.character = codePageMap88591ToDOS[input.character & 0x7f];
}
if (scummVMKeycode == Common::KEYCODE_TAB) {
input.character = SCI_KEY_TAB;
@@ -302,6 +323,11 @@ SciEvent EventManager::getScummVMEvent() {
input.character = altify(input.character);
if (getSciVersion() <= SCI_VERSION_1_MIDDLE && (scummVMKeyFlags & Common::KBD_CTRL) && input.character > 0 && input.character < 27)
input.character += 96; // 0x01 -> 'a'
+#ifdef ENABLE_SCI32
+ if (getSciVersion() >= SCI_VERSION_2 && (scummVMKeyFlags & Common::KBD_CTRL) && input.character == 'c') {
+ input.character = SCI_KEY_ETX;
+ }
+#endif
// If no actual key was pressed (e.g. if only a modifier key was pressed),
// ignore the event
@@ -328,8 +354,12 @@ void EventManager::updateScreen() {
}
}
-SciEvent EventManager::getSciEvent(unsigned int mask) {
- SciEvent event = { SCI_EVENT_NONE, 0, 0, Common::Point(0, 0) };
+SciEvent EventManager::getSciEvent(uint32 mask) {
+#ifdef ENABLE_SCI32
+ SciEvent event = { SCI_EVENT_NONE, 0, 0, Common::Point(), Common::Point() };
+#else
+ SciEvent event = { SCI_EVENT_NONE, 0, 0, Common::Point() };
+#endif
EventManager::updateScreen();
@@ -342,7 +372,7 @@ SciEvent EventManager::getSciEvent(unsigned int mask) {
// Search for matching event in queue
Common::List<SciEvent>::iterator iter = _events.begin();
- while (iter != _events.end() && !((*iter).type & mask))
+ while (iter != _events.end() && !(iter->type & mask))
++iter;
if (iter != _events.end()) {
@@ -364,17 +394,17 @@ SciEvent EventManager::getSciEvent(unsigned int mask) {
void SciEngine::sleep(uint32 msecs) {
uint32 time;
- const uint32 wakeup_time = g_system->getMillis() + msecs;
+ const uint32 wakeUpTime = g_system->getMillis() + msecs;
while (true) {
// let backend process events and update the screen
_eventMan->getSciEvent(SCI_EVENT_PEEK);
time = g_system->getMillis();
- if (time + 10 < wakeup_time) {
+ if (time + 10 < wakeUpTime) {
g_system->delayMillis(10);
} else {
- if (time < wakeup_time)
- g_system->delayMillis(wakeup_time - time);
+ if (time < wakeUpTime)
+ g_system->delayMillis(wakeUpTime - time);
break;
}
diff --git a/engines/sci/event.h b/engines/sci/event.h
index 76c884aba4..15a94b3e73 100644
--- a/engines/sci/event.h
+++ b/engines/sci/event.h
@@ -39,11 +39,18 @@ struct SciEvent {
uint16 character;
/**
- * The mouse position at the time the event was created.
- *
- * These are display coordinates!
+ * The mouse position at the time the event was created,
+ * in display coordinates.
*/
Common::Point mousePos;
+
+#ifdef ENABLE_SCI32
+ /**
+ * The mouse position at the time the event was created,
+ * in script coordinates.
+ */
+ Common::Point mousePosSci;
+#endif
};
/*Values for type*/
@@ -59,6 +66,9 @@ struct SciEvent {
#define SCI_EVENT_ANY 0x7fff
/* Keycodes of special keys: */
+#ifdef ENABLE_SCI32
+#define SCI_KEY_ETX 3
+#endif
#define SCI_KEY_ESC 27
#define SCI_KEY_BACKSPACE 8
#define SCI_KEY_ENTER 13
@@ -121,7 +131,7 @@ public:
~EventManager();
void updateScreen();
- SciEvent getSciEvent(unsigned int mask);
+ SciEvent getSciEvent(uint32 mask);
private:
SciEvent getScummVMEvent();
diff --git a/engines/sci/graphics/animate.cpp b/engines/sci/graphics/animate.cpp
index 7957ed6a55..98278397b7 100644
--- a/engines/sci/graphics/animate.cpp
+++ b/engines/sci/graphics/animate.cpp
@@ -28,6 +28,7 @@
#include "sci/sci.h"
#include "sci/event.h"
#include "sci/engine/kernel.h"
+#include "sci/engine/script_patches.h"
#include "sci/engine/state.h"
#include "sci/engine/selector.h"
#include "sci/engine/vm.h"
@@ -44,8 +45,8 @@
namespace Sci {
-GfxAnimate::GfxAnimate(EngineState *state, GfxCache *cache, GfxPorts *ports, GfxPaint16 *paint16, GfxScreen *screen, GfxPalette *palette, GfxCursor *cursor, GfxTransitions *transitions)
- : _s(state), _cache(cache), _ports(ports), _paint16(paint16), _screen(screen), _palette(palette), _cursor(cursor), _transitions(transitions) {
+GfxAnimate::GfxAnimate(EngineState *state, ScriptPatcher *scriptPatcher, GfxCache *cache, GfxPorts *ports, GfxPaint16 *paint16, GfxScreen *screen, GfxPalette *palette, GfxCursor *cursor, GfxTransitions *transitions)
+ : _s(state), _scriptPatcher(scriptPatcher), _cache(cache), _ports(ports), _paint16(paint16), _screen(screen), _palette(palette), _cursor(cursor), _transitions(transitions) {
init();
}
@@ -55,16 +56,77 @@ GfxAnimate::~GfxAnimate() {
void GfxAnimate::init() {
_lastCastData.clear();
- _ignoreFastCast = false;
- // fastCast object is not found in any SCI games prior SCI1
- if (getSciVersion() <= SCI_VERSION_01)
- _ignoreFastCast = true;
- // Also if fastCast object exists at gamestartup, we can assume that the interpreter doesnt do kAnimate aborts
- // (found in Larry 1)
- if (getSciVersion() > SCI_VERSION_0_EARLY) {
- if (!_s->_segMan->findObjectByName("fastCast").isNull())
- _ignoreFastCast = true;
+ _fastCastEnabled = false;
+ if (getSciVersion() == SCI_VERSION_1_1) {
+ // Seems to have been available for all SCI1.1 games
+ _fastCastEnabled = true;
+ } else if (getSciVersion() >= SCI_VERSION_1_EARLY) {
+ // fastCast only exists for some games between SCI1 early and SCI1 late
+ // Try to detect it by code signature
+ // It's extremely important, that we only enable it for games that actually need it
+ if (detectFastCast()) {
+ _fastCastEnabled = true;
+ }
+ }
+}
+
+// Signature for fastCast detection
+static const uint16 fastCastSignature[] = {
+ SIG_MAGICDWORD,
+ 0x35, 0x00, // ldi 00
+ 0xa1, 84, // sag global[84d]
+ SIG_END
+};
+
+// Fast cast in games:
+
+// SCI1 Early:
+// KQ5 - no fastcast, LSL1 (demo) - no fastcast, Mixed Up Fairy Tales - *has fastcast*, XMas Card 1990 - no fastcast,
+// SQ4Floppy - no fastcast, Mixed Up Mother Goose - no fastcast
+//
+// SCI1 Middle:
+// LSL5 demo - no fastfast, Conquest of the Longbow demo - no fastcast, LSL1 - no fastcast,
+// Astro Chicken II - no fastcast
+//
+// SCI1 Late:
+// Castle of Dr. Brain demo - has fastcast, Castle of Dr. Brain - has fastcast,
+// Conquests of the Longbow - has fastcast, Space Quest 1 EGA - has fastcast,
+// King's Quest 5 multilingual - *NO* fastcast, Police Quest 3 demo - *NO* fastcast,
+// LSL5 multilingual - has fastcast, Police Quest 3 - has fastcast,
+// EcoQuest 1 - has fastcast, Mixed Up Fairy Tales demo - has fastcast,
+// Space Quest 4 multilingual - *NO* fastcast
+//
+// SCI1.1
+// Quest for Glory 3 demo - has fastcast, Police Quest 1 - hast fastcast, Quest for Glory 1 - has fastcast
+// Laura Bow 2 Floppy - has fastcast, Mixed Up Mother Goose - has fastcast, Quest for Glory 3 - has fastcast
+// Island of Dr. Brain - has fastcast, King's Quest 6 - has fastcast, Space Quest 5 - has fastcast
+// Hoyle 4 - has fastcast, Laura Bow 2 CD - has fastcast, Freddy Pharkas CD - has fastcast
+bool GfxAnimate::detectFastCast() {
+ SegManager *segMan = _s->_segMan;
+ const reg_t gameVMObject = g_sci->getGameObject();
+ reg_t gameSuperVMObject = segMan->getObject(gameVMObject)->getSuperClassSelector();
+ uint32 magicDWord = 0; // platform-specific BE/LE for performance
+ int magicDWordOffset = 0;
+
+ if (gameSuperVMObject.isNull()) {
+ gameSuperVMObject = gameVMObject; // Just in case. According to sci.cpp this may happen in KQ5CD, when loading saved games before r54510
+ }
+
+ Script *objectScript = segMan->getScript(gameSuperVMObject.getSegment());
+ byte *scriptData = const_cast<byte *>(objectScript->getBuf(0));
+ uint32 scriptSize = objectScript->getBufSize();
+
+ _scriptPatcher->calculateMagicDWordAndVerify("fast cast detection", fastCastSignature, true, magicDWord, magicDWordOffset);
+
+ // Signature is found for multilingual King's Quest 5 too, but it looks as if the fast cast global is never set
+ // within that game. Which means even though we detect it as having the capability, it's never actually used.
+ // The original multilingual KQ5 interpreter did have this feature disabled.
+ // Sierra probably used latest system scripts and that's why we detect it.
+ if (_scriptPatcher->findSignature(magicDWord, magicDWordOffset, fastCastSignature, "fast cast detection", scriptData, scriptSize) >= 0) {
+ // Signature found, game seems to use fast cast for kAnimate
+ return true;
}
+ return false;
}
void GfxAnimate::disposeLastCast() {
@@ -80,12 +142,14 @@ bool GfxAnimate::invoke(List *list, int argc, reg_t *argv) {
while (curNode) {
curObject = curNode->value;
- if (!_ignoreFastCast) {
+ if (_fastCastEnabled) {
// Check if the game has a fastCast object set
// if we don't abort kAnimate processing, at least in kq5 there will be animation cels drawn into speech boxes.
if (!_s->variables[VAR_GLOBAL][84].isNull()) {
- if (!strcmp(_s->_segMan->getObjectName(_s->variables[VAR_GLOBAL][84]), "fastCast"))
- return false;
+ // This normally points to an object called "fastCast",
+ // but for example in Eco Quest 1 it may also point to an object called "EventHandler" (see bug #5170)
+ // Original SCI only checked, if this global was not 0.
+ return false;
}
}
diff --git a/engines/sci/graphics/animate.h b/engines/sci/graphics/animate.h
index 6c1822c903..ac7078093c 100644
--- a/engines/sci/graphics/animate.h
+++ b/engines/sci/graphics/animate.h
@@ -87,9 +87,13 @@ class GfxView;
*/
class GfxAnimate {
public:
- GfxAnimate(EngineState *state, GfxCache *cache, GfxPorts *ports, GfxPaint16 *paint16, GfxScreen *screen, GfxPalette *palette, GfxCursor *cursor, GfxTransitions *transitions);
+ GfxAnimate(EngineState *state, ScriptPatcher *scriptPatcher, GfxCache *cache, GfxPorts *ports, GfxPaint16 *paint16, GfxScreen *screen, GfxPalette *palette, GfxCursor *cursor, GfxTransitions *transitions);
virtual ~GfxAnimate();
+ bool isFastCastEnabled() {
+ return _fastCastEnabled;
+ }
+
void disposeLastCast();
bool invoke(List *list, int argc, reg_t *argv);
void makeSortedList(List *list);
@@ -110,6 +114,7 @@ public:
private:
void init();
+ bool detectFastCast();
void addToPicSetPicNotValid();
void animateShowPic();
@@ -119,6 +124,7 @@ private:
void setNsRect(GfxView *view, AnimateList::iterator it);
EngineState *_s;
+ ScriptPatcher *_scriptPatcher;
GfxCache *_cache;
GfxPorts *_ports;
GfxPaint16 *_paint16;
@@ -130,7 +136,7 @@ private:
AnimateList _list;
AnimateArray _lastCastData;
- bool _ignoreFastCast;
+ bool _fastCastEnabled;
};
} // End of namespace Sci
diff --git a/engines/sci/graphics/cache.cpp b/engines/sci/graphics/cache.cpp
index 59af8334eb..fb1f557ad6 100644
--- a/engines/sci/graphics/cache.cpp
+++ b/engines/sci/graphics/cache.cpp
@@ -102,8 +102,4 @@ int16 GfxCache::kernelViewGetCelCount(GuiResourceId viewId, int16 loopNo) {
return getView(viewId)->getCelCount(loopNo);
}
-byte GfxCache::kernelViewGetColorAtCoordinate(GuiResourceId viewId, int16 loopNo, int16 celNo, int16 x, int16 y) {
- return getView(viewId)->getColorAtCoordinate(loopNo, celNo, x, y);
-}
-
} // End of namespace Sci
diff --git a/engines/sci/graphics/cache.h b/engines/sci/graphics/cache.h
index 33fa4fe399..61952718a9 100644
--- a/engines/sci/graphics/cache.h
+++ b/engines/sci/graphics/cache.h
@@ -49,8 +49,6 @@ public:
int16 kernelViewGetLoopCount(GuiResourceId viewId);
int16 kernelViewGetCelCount(GuiResourceId viewId, int16 loopNo);
- byte kernelViewGetColorAtCoordinate(GuiResourceId viewId, int16 loopNo, int16 celNo, int16 x, int16 y);
-
private:
void purgeFontCache();
void purgeViewCache();
diff --git a/engines/sci/graphics/celobj32.cpp b/engines/sci/graphics/celobj32.cpp
new file mode 100644
index 0000000000..693bc5f196
--- /dev/null
+++ b/engines/sci/graphics/celobj32.cpp
@@ -0,0 +1,1050 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 "sci/resource.h"
+#include "sci/engine/seg_manager.h"
+#include "sci/engine/state.h"
+#include "sci/graphics/celobj32.h"
+#include "sci/graphics/frameout.h"
+#include "sci/graphics/palette32.h"
+#include "sci/graphics/picture.h"
+#include "sci/graphics/remap.h"
+#include "sci/graphics/text32.h"
+#include "sci/graphics/view.h"
+
+namespace Sci {
+#pragma mark CelScaler
+CelScaler *CelObj::_scaler = nullptr;
+
+void CelScaler::activateScaleTables(const Ratio &scaleX, const Ratio &scaleY) {
+ const int16 screenWidth = g_sci->_gfxFrameout->getCurrentBuffer().screenWidth;
+ const int16 screenHeight = g_sci->_gfxFrameout->getCurrentBuffer().screenHeight;
+
+ for (int i = 0; i < ARRAYSIZE(_scaleTables); ++i) {
+ if (_scaleTables[i].scaleX == scaleX && _scaleTables[i].scaleY == scaleY) {
+ _activeIndex = i;
+ return;
+ }
+ }
+
+ int i = 1 - _activeIndex;
+ _activeIndex = i;
+ CelScalerTable &table = _scaleTables[i];
+
+ if (table.scaleX != scaleX) {
+ assert(screenWidth <= ARRAYSIZE(table.valuesX));
+ buildLookupTable(table.valuesX, scaleX, screenWidth);
+ table.scaleX = scaleX;
+ }
+
+ if (table.scaleY != scaleY) {
+ assert(screenHeight <= ARRAYSIZE(table.valuesY));
+ buildLookupTable(table.valuesY, scaleY, screenHeight);
+ table.scaleY = scaleY;
+ }
+}
+
+void CelScaler::buildLookupTable(int *table, const Ratio &ratio, const int size) {
+ int value = 0;
+ int remainder = 0;
+ int num = ratio.getNumerator();
+ for (int i = 0; i < size; ++i) {
+ *table++ = value;
+ remainder += ratio.getDenominator();
+ if (remainder >= num) {
+ value += remainder / num;
+ remainder %= num;
+ }
+ }
+}
+
+const CelScalerTable *CelScaler::getScalerTable(const Ratio &scaleX, const Ratio &scaleY) {
+ activateScaleTables(scaleX, scaleY);
+ return &_scaleTables[_activeIndex];
+}
+
+#pragma mark -
+#pragma mark CelObj
+
+void CelObj::init() {
+ CelObj::deinit();
+ _nextCacheId = 1;
+ _scaler = new CelScaler();
+ _cache = new CelCache;
+ _cache->resize(100);
+}
+
+void CelObj::deinit() {
+ delete _scaler;
+ _scaler = nullptr;
+ if (_cache != nullptr) {
+ for (CelCache::iterator it = _cache->begin(); it != _cache->end(); ++it) {
+ delete it->celObj;
+ }
+ }
+ delete _cache;
+ _cache = nullptr;
+}
+
+#pragma mark -
+#pragma mark CelObj - Scalers
+
+template<bool FLIP, typename READER>
+struct SCALER_NoScale {
+#ifndef NDEBUG
+ const byte *_rowEdge;
+#endif
+ const byte *_row;
+ READER _reader;
+ const int16 _lastIndex;
+ const int16 _sourceX;
+ const int16 _sourceY;
+
+ SCALER_NoScale(const CelObj &celObj, const int16 maxWidth, const Common::Point &scaledPosition) :
+ _reader(celObj, FLIP ? celObj._width : maxWidth),
+ _lastIndex(celObj._width - 1),
+ _sourceX(scaledPosition.x),
+ _sourceY(scaledPosition.y) {}
+
+ inline void setTarget(const int16 x, const int16 y) {
+ _row = _reader.getRow(y - _sourceY);
+
+ if (FLIP) {
+#ifndef NDEBUG
+ _rowEdge = _row - 1;
+#endif
+ _row += _lastIndex - (x - _sourceX);
+ assert(_row > _rowEdge);
+ } else {
+#ifndef NDEBUG
+ _rowEdge = _row + _lastIndex + 1;
+#endif
+ _row += x - _sourceX;
+ assert(_row < _rowEdge);
+ }
+ }
+
+ inline byte read() {
+ assert(_row != _rowEdge);
+
+ if (FLIP) {
+ return *_row--;
+ } else {
+ return *_row++;
+ }
+ }
+};
+
+template<bool FLIP, typename READER>
+struct SCALER_Scale {
+#ifndef NDEBUG
+ int16 _maxX;
+#endif
+ const byte *_row;
+ READER _reader;
+ int16 _x;
+ static int16 _valuesX[1024];
+ static int16 _valuesY[1024];
+
+ SCALER_Scale(const CelObj &celObj, const Common::Rect &targetRect, const Common::Point &scaledPosition, const Ratio scaleX, const Ratio scaleY) :
+#ifndef NDEBUG
+ _maxX(targetRect.right - 1),
+#endif
+ // The maximum width of the scaled object may not be as
+ // wide as the source data it requires if downscaling,
+ // so just always make the reader decompress an entire
+ // line of source data when scaling
+ _reader(celObj, celObj._width) {
+ // In order for scaling ratios to apply equally across objects that
+ // start at different positions on the screen, the pixels that are
+ // read from the source bitmap must all use the same pattern of
+ // division. In other words, cels must follow the same scaling pattern
+ // as if they were drawn starting at an even multiple of the scaling
+ // ratio, even if they were not.
+ //
+ // To get the correct source pixel when reading out through the scaler,
+ // the engine creates a lookup table for each axis that translates
+ // directly from target positions to the indexes of source pixels using
+ // the global cadence for the given scaling ratio.
+
+ const CelScalerTable *table = CelObj::_scaler->getScalerTable(scaleX, scaleY);
+
+ const int16 unscaledX = (scaledPosition.x / scaleX).toInt();
+ if (FLIP) {
+ int lastIndex = celObj._width - 1;
+ for (int16 x = targetRect.left; x < targetRect.right; ++x) {
+ _valuesX[x] = lastIndex - (table->valuesX[x] - unscaledX);
+ }
+ } else {
+ for (int16 x = targetRect.left; x < targetRect.right; ++x) {
+ _valuesX[x] = table->valuesX[x] - unscaledX;
+ }
+ }
+
+ const int16 unscaledY = (scaledPosition.y / scaleY).toInt();
+ for (int16 y = targetRect.top; y < targetRect.bottom; ++y) {
+ _valuesY[y] = table->valuesY[y] - unscaledY;
+ }
+ }
+
+ inline void setTarget(const int16 x, const int16 y) {
+ _row = _reader.getRow(_valuesY[y]);
+ _x = x;
+ assert(_x >= 0 && _x <= _maxX);
+ }
+
+ inline byte read() {
+ assert(_x >= 0 && _x <= _maxX);
+ return _row[_valuesX[_x++]];
+ }
+};
+
+template<bool FLIP, typename READER>
+int16 SCALER_Scale<FLIP, READER>::_valuesX[1024];
+template<bool FLIP, typename READER>
+int16 SCALER_Scale<FLIP, READER>::_valuesY[1024];
+
+#pragma mark -
+#pragma mark CelObj - Resource readers
+
+struct READER_Uncompressed {
+private:
+#ifndef NDEBUG
+ const int16 _sourceHeight;
+#endif
+ byte *_pixels;
+ const int16 _sourceWidth;
+
+public:
+ READER_Uncompressed(const CelObj &celObj, const int16) :
+#ifndef NDEBUG
+ _sourceHeight(celObj._height),
+#endif
+ _sourceWidth(celObj._width) {
+ byte *resource = celObj.getResPointer();
+ _pixels = resource + READ_SCI11ENDIAN_UINT32(resource + celObj._celHeaderOffset + 24);
+ }
+
+ inline const byte *getRow(const int16 y) const {
+ assert(y >= 0 && y < _sourceHeight);
+ return _pixels + y * _sourceWidth;
+ }
+};
+
+struct READER_Compressed {
+private:
+ byte *_resource;
+ byte _buffer[1024];
+ uint32 _controlOffset;
+ uint32 _dataOffset;
+ uint32 _uncompressedDataOffset;
+ int16 _y;
+ const int16 _sourceHeight;
+ const uint8 _transparentColor;
+ const int16 _maxWidth;
+
+public:
+ READER_Compressed(const CelObj &celObj, const int16 maxWidth) :
+ _resource(celObj.getResPointer()),
+ _y(-1),
+ _sourceHeight(celObj._height),
+ _transparentColor(celObj._transparentColor),
+ _maxWidth(maxWidth) {
+ assert(maxWidth <= celObj._width);
+
+ byte *celHeader = _resource + celObj._celHeaderOffset;
+ _dataOffset = READ_SCI11ENDIAN_UINT32(celHeader + 24);
+ _uncompressedDataOffset = READ_SCI11ENDIAN_UINT32(celHeader + 28);
+ _controlOffset = READ_SCI11ENDIAN_UINT32(celHeader + 32);
+ }
+
+ inline const byte *getRow(const int16 y) {
+ assert(y >= 0 && y < _sourceHeight);
+ if (y != _y) {
+ // compressed data segment for row
+ byte *row = _resource + _dataOffset + READ_SCI11ENDIAN_UINT32(_resource + _controlOffset + y * 4);
+
+ // uncompressed data segment for row
+ byte *literal = _resource + _uncompressedDataOffset + READ_SCI11ENDIAN_UINT32(_resource + _controlOffset + _sourceHeight * 4 + y * 4);
+
+ uint8 length;
+ for (int16 i = 0; i < _maxWidth; i += length) {
+ byte controlByte = *row++;
+ length = controlByte;
+
+ // Run-length encoded
+ if (controlByte & 0x80) {
+ length &= 0x3F;
+ assert(i + length < (int)sizeof(_buffer));
+
+ // Fill with skip color
+ if (controlByte & 0x40) {
+ memset(_buffer + i, _transparentColor, length);
+ // Next value is fill color
+ } else {
+ memset(_buffer + i, *literal, length);
+ ++literal;
+ }
+ // Uncompressed
+ } else {
+ assert(i + length < (int)sizeof(_buffer));
+ memcpy(_buffer + i, literal, length);
+ literal += length;
+ }
+ }
+ _y = y;
+ }
+
+ return _buffer;
+ }
+};
+
+#pragma mark -
+#pragma mark CelObj - Remappers
+
+struct MAPPER_NoMD {
+ inline void draw(byte *target, const byte pixel, const uint8 skipColor) const {
+ if (pixel != skipColor) {
+ *target = pixel;
+ }
+ }
+};
+struct MAPPER_NoMDNoSkip {
+ inline void draw(byte *target, const byte pixel, const uint8) const {
+ *target = pixel;
+ }
+};
+
+struct MAPPER_Map {
+ inline void draw(byte *target, const byte pixel, const uint8 skipColor) const {
+ if (pixel != skipColor) {
+ if (pixel < g_sci->_gfxRemap32->getStartColor()) {
+ *target = pixel;
+ } else {
+ if (g_sci->_gfxRemap32->remapEnabled(pixel))
+ *target = g_sci->_gfxRemap32->remapColor(pixel, *target);
+ }
+ }
+ }
+};
+
+void CelObj::draw(Buffer &target, const ScreenItem &screenItem, const Common::Rect &targetRect) const {
+ const Common::Point &scaledPosition = screenItem._scaledPosition;
+ const Ratio &scaleX = screenItem._ratioX;
+ const Ratio &scaleY = screenItem._ratioY;
+
+ if (_remap) {
+ // NOTE: In the original code this check was `g_Remap_numActiveRemaps && _remap`,
+ // but since we are already in a `_remap` branch, there is no reason to check it
+ // again
+ if (g_sci->_gfxRemap32->getRemapCount()) {
+ if (scaleX.isOne() && scaleY.isOne()) {
+ if (_compressionType == kCelCompressionNone) {
+ if (_drawMirrored) {
+ drawUncompHzFlipMap(target, targetRect, scaledPosition);
+ } else {
+ drawUncompNoFlipMap(target, targetRect, scaledPosition);
+ }
+ } else {
+ if (_drawMirrored) {
+ drawHzFlipMap(target, targetRect, scaledPosition);
+ } else {
+ drawNoFlipMap(target, targetRect, scaledPosition);
+ }
+ }
+ } else {
+ if (_compressionType == kCelCompressionNone) {
+ scaleDrawUncompMap(target, scaleX, scaleY, targetRect, scaledPosition);
+ } else {
+ scaleDrawMap(target, scaleX, scaleY, targetRect, scaledPosition);
+ }
+ }
+ } else {
+ if (scaleX.isOne() && scaleY.isOne()) {
+ if (_compressionType == kCelCompressionNone) {
+ if (_drawMirrored) {
+ drawUncompHzFlip(target, targetRect, scaledPosition);
+ } else {
+ drawUncompNoFlip(target, targetRect, scaledPosition);
+ }
+ } else {
+ if (_drawMirrored) {
+ drawHzFlip(target, targetRect, scaledPosition);
+ } else {
+ drawNoFlip(target, targetRect, scaledPosition);
+ }
+ }
+ } else {
+ if (_compressionType == kCelCompressionNone) {
+ scaleDrawUncomp(target, scaleX, scaleY, targetRect, scaledPosition);
+ } else {
+ scaleDraw(target, scaleX, scaleY, targetRect, scaledPosition);
+ }
+ }
+ }
+ } else {
+ if (scaleX.isOne() && scaleY.isOne()) {
+ if (_compressionType == kCelCompressionNone) {
+ if (_transparent) {
+ if (_drawMirrored) {
+ drawUncompHzFlipNoMD(target, targetRect, scaledPosition);
+ } else {
+ drawUncompNoFlipNoMD(target, targetRect, scaledPosition);
+ }
+ } else {
+ if (_drawMirrored) {
+ drawUncompHzFlipNoMDNoSkip(target, targetRect, scaledPosition);
+ } else {
+ drawUncompNoFlipNoMDNoSkip(target, targetRect, scaledPosition);
+ }
+ }
+ } else {
+ if (_drawMirrored) {
+ drawHzFlipNoMD(target, targetRect, scaledPosition);
+ } else {
+ drawNoFlipNoMD(target, targetRect, scaledPosition);
+ }
+ }
+ } else {
+ if (_compressionType == kCelCompressionNone) {
+ scaleDrawUncompNoMD(target, scaleX, scaleY, targetRect, scaledPosition);
+ } else {
+ scaleDrawNoMD(target, scaleX, scaleY, targetRect, scaledPosition);
+ }
+ }
+ }
+}
+
+void CelObj::draw(Buffer &target, const ScreenItem &screenItem, const Common::Rect &targetRect, bool mirrorX) {
+ _drawMirrored = mirrorX;
+ draw(target, screenItem, targetRect);
+}
+
+void CelObj::draw(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition, const bool mirrorX) {
+ _drawMirrored = mirrorX;
+ Ratio square;
+ drawTo(target, targetRect, scaledPosition, square, square);
+}
+
+void CelObj::drawTo(Buffer &target, Common::Rect const &targetRect, Common::Point const &scaledPosition, Ratio const &scaleX, Ratio const &scaleY) const {
+ if (_remap) {
+ if (scaleX.isOne() && scaleY.isOne()) {
+ if (_compressionType == kCelCompressionNone) {
+ if (_drawMirrored) {
+ drawUncompHzFlipMap(target, targetRect, scaledPosition);
+ } else {
+ drawUncompNoFlipMap(target, targetRect, scaledPosition);
+ }
+ } else {
+ if (_drawMirrored) {
+ drawHzFlipMap(target, targetRect, scaledPosition);
+ } else {
+ drawNoFlipMap(target, targetRect, scaledPosition);
+ }
+ }
+ } else {
+ if (_compressionType == kCelCompressionNone) {
+ scaleDrawUncompMap(target, scaleX, scaleY, targetRect, scaledPosition);
+ } else {
+ scaleDrawMap(target, scaleX, scaleY, targetRect, scaledPosition);
+ }
+ }
+ } else {
+ if (scaleX.isOne() && scaleY.isOne()) {
+ if (_compressionType == kCelCompressionNone) {
+ if (_drawMirrored) {
+ drawUncompHzFlipNoMD(target, targetRect, scaledPosition);
+ } else {
+ drawUncompNoFlipNoMD(target, targetRect, scaledPosition);
+ }
+ } else {
+ if (_drawMirrored) {
+ drawHzFlipNoMD(target, targetRect, scaledPosition);
+ } else {
+ drawNoFlipNoMD(target, targetRect, scaledPosition);
+ }
+ }
+ } else {
+ if (_compressionType == kCelCompressionNone) {
+ scaleDrawUncompNoMD(target, scaleX, scaleY, targetRect, scaledPosition);
+ } else {
+ scaleDrawNoMD(target, scaleX, scaleY, targetRect, scaledPosition);
+ }
+ }
+ }
+}
+
+uint8 CelObj::readPixel(uint16 x, const uint16 y, bool mirrorX) const {
+ if (mirrorX) {
+ x = _width - x - 1;
+ }
+
+ if (_compressionType == kCelCompressionNone) {
+ READER_Uncompressed reader(*this, x + 1);
+ return reader.getRow(y)[x];
+ } else {
+ READER_Compressed reader(*this, x + 1);
+ return reader.getRow(y)[x];
+ }
+}
+
+void CelObj::submitPalette() const {
+ if (_hunkPaletteOffset) {
+ Palette palette;
+
+ byte *res = getResPointer();
+ // NOTE: In SCI engine this uses HunkPalette::Init.
+ // TODO: Use a better size value
+ g_sci->_gfxPalette32->createFromData(res + _hunkPaletteOffset, 999999, &palette);
+ g_sci->_gfxPalette32->submit(palette);
+ }
+}
+
+#pragma mark -
+#pragma mark CelObj - Caching
+int CelObj::_nextCacheId = 1;
+CelCache *CelObj::_cache = nullptr;
+
+int CelObj::searchCache(const CelInfo32 &celInfo, int *nextInsertIndex) const {
+ int oldestId = _nextCacheId + 1;
+ int oldestIndex = -1;
+
+ for (int i = 0, len = _cache->size(); i < len; ++i) {
+ CelCacheEntry &entry = (*_cache)[i];
+
+ if (entry.celObj != nullptr) {
+ if (entry.celObj->_info == celInfo) {
+ entry.id = ++_nextCacheId;
+ return i;
+ }
+
+ if (oldestId > entry.id) {
+ oldestId = entry.id;
+ oldestIndex = i;
+ }
+ } else if (oldestIndex == -1) {
+ oldestIndex = i;
+ }
+ }
+
+ // NOTE: Unlike the original SCI engine code, the out-param
+ // here is only updated if there was not a cache hit.
+ *nextInsertIndex = oldestIndex;
+ return -1;
+}
+
+void CelObj::putCopyInCache(const int cacheIndex) const {
+ if (cacheIndex == -1) {
+ error("Invalid cache index");
+ }
+
+ CelCacheEntry &entry = (*_cache)[cacheIndex];
+
+ if (entry.celObj != nullptr) {
+ delete entry.celObj;
+ }
+
+ entry.celObj = duplicate();
+ entry.id = ++_nextCacheId;
+}
+
+#pragma mark -
+#pragma mark CelObj - Drawing
+
+template<typename MAPPER, typename SCALER>
+struct RENDERER {
+ MAPPER &_mapper;
+ SCALER &_scaler;
+ const uint8 _skipColor;
+
+ RENDERER(MAPPER &mapper, SCALER &scaler, const uint8 skipColor) :
+ _mapper(mapper),
+ _scaler(scaler),
+ _skipColor(skipColor) {}
+
+ inline void draw(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const {
+ byte *targetPixel = (byte *)target.getPixels() + target.screenWidth * targetRect.top + targetRect.left;
+
+ const int16 skipStride = target.screenWidth - targetRect.width();
+ const int16 targetWidth = targetRect.width();
+ const int16 targetHeight = targetRect.height();
+ for (int16 y = 0; y < targetHeight; ++y) {
+ _scaler.setTarget(targetRect.left, targetRect.top + y);
+
+ for (int16 x = 0; x < targetWidth; ++x) {
+ _mapper.draw(targetPixel++, _scaler.read(), _skipColor);
+ }
+
+ targetPixel += skipStride;
+ }
+ }
+};
+
+template<typename MAPPER, typename SCALER>
+void CelObj::render(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const {
+
+ MAPPER mapper;
+ SCALER scaler(*this, targetRect.left - scaledPosition.x + targetRect.width(), scaledPosition);
+ RENDERER<MAPPER, SCALER> renderer(mapper, scaler, _transparentColor);
+ renderer.draw(target, targetRect, scaledPosition);
+}
+
+template<typename MAPPER, typename SCALER>
+void CelObj::render(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition, const Ratio &scaleX, const Ratio &scaleY) const {
+
+ MAPPER mapper;
+ SCALER scaler(*this, targetRect, scaledPosition, scaleX, scaleY);
+ RENDERER<MAPPER, SCALER> renderer(mapper, scaler, _transparentColor);
+ renderer.draw(target, targetRect, scaledPosition);
+}
+
+void dummyFill(Buffer &target, const Common::Rect &targetRect) {
+ target.fillRect(targetRect, 250);
+}
+
+void CelObj::drawHzFlip(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const {
+ debug("drawHzFlip");
+ dummyFill(target, targetRect);
+}
+
+void CelObj::drawNoFlip(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const {
+ debug("drawNoFlip");
+ dummyFill(target, targetRect);
+}
+
+void CelObj::drawUncompNoFlip(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const {
+ debug("drawUncompNoFlip");
+ dummyFill(target, targetRect);
+}
+
+void CelObj::drawUncompHzFlip(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const {
+ debug("drawUncompHzFlip");
+ dummyFill(target, targetRect);
+}
+
+void CelObj::scaleDraw(Buffer &target, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition) const {
+ debug("scaleDraw");
+ dummyFill(target, targetRect);
+}
+
+void CelObj::scaleDrawUncomp(Buffer &target, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition) const {
+ debug("scaleDrawUncomp");
+ dummyFill(target, targetRect);
+}
+
+void CelObj::drawHzFlipMap(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const {
+ render<MAPPER_Map, SCALER_NoScale<true, READER_Compressed> >(target, targetRect, scaledPosition);
+}
+
+void CelObj::drawNoFlipMap(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const {
+ render<MAPPER_Map, SCALER_NoScale<false, READER_Compressed> >(target, targetRect, scaledPosition);
+}
+
+void CelObj::drawUncompNoFlipMap(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const {
+ render<MAPPER_Map, SCALER_NoScale<false, READER_Uncompressed> >(target, targetRect, scaledPosition);
+}
+
+void CelObj::drawUncompHzFlipMap(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const {
+ render<MAPPER_Map, SCALER_NoScale<true, READER_Uncompressed> >(target, targetRect, scaledPosition);
+}
+
+void CelObj::scaleDrawMap(Buffer &target, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition) const {
+ if (_drawMirrored)
+ render<MAPPER_Map, SCALER_Scale<true, READER_Compressed> >(target, targetRect, scaledPosition, scaleX, scaleY);
+ else
+ render<MAPPER_Map, SCALER_Scale<false, READER_Compressed> >(target, targetRect, scaledPosition, scaleX, scaleY);
+}
+
+void CelObj::scaleDrawUncompMap(Buffer &target, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition) const {
+ if (_drawMirrored)
+ render<MAPPER_Map, SCALER_Scale<true, READER_Uncompressed> >(target, targetRect, scaledPosition, scaleX, scaleY);
+ else
+ render<MAPPER_Map, SCALER_Scale<false, READER_Uncompressed> >(target, targetRect, scaledPosition, scaleX, scaleY);
+}
+
+void CelObj::drawNoFlipNoMD(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const {
+ render<MAPPER_NoMD, SCALER_NoScale<false, READER_Compressed> >(target, targetRect, scaledPosition);
+}
+
+void CelObj::drawHzFlipNoMD(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const {
+ render<MAPPER_NoMD, SCALER_NoScale<true, READER_Compressed> >(target, targetRect, scaledPosition);
+}
+
+void CelObj::drawUncompNoFlipNoMD(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const {
+ render<MAPPER_NoMD, SCALER_NoScale<false, READER_Uncompressed> >(target, targetRect, scaledPosition);
+}
+
+void CelObj::drawUncompNoFlipNoMDNoSkip(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const {
+ render<MAPPER_NoMDNoSkip, SCALER_NoScale<false, READER_Uncompressed> >(target, targetRect, scaledPosition);
+}
+
+void CelObj::drawUncompHzFlipNoMD(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const {
+ render<MAPPER_NoMD, SCALER_NoScale<true, READER_Uncompressed> >(target, targetRect, scaledPosition);
+}
+
+void CelObj::drawUncompHzFlipNoMDNoSkip(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const {
+ render<MAPPER_NoMDNoSkip, SCALER_NoScale<true, READER_Uncompressed> >(target, targetRect, scaledPosition);
+}
+
+void CelObj::scaleDrawNoMD(Buffer &target, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition) const {
+ if (_drawMirrored)
+ render<MAPPER_NoMD, SCALER_Scale<true, READER_Compressed> >(target, targetRect, scaledPosition, scaleX, scaleY);
+ else
+ render<MAPPER_NoMD, SCALER_Scale<false, READER_Compressed> >(target, targetRect, scaledPosition, scaleX, scaleY);
+}
+
+void CelObj::scaleDrawUncompNoMD(Buffer &target, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition) const {
+ if (_drawMirrored)
+ render<MAPPER_NoMD, SCALER_Scale<true, READER_Uncompressed> >(target, targetRect, scaledPosition, scaleX, scaleY);
+ else
+ render<MAPPER_NoMD, SCALER_Scale<false, READER_Uncompressed> >(target, targetRect, scaledPosition, scaleX, scaleY);
+}
+
+#pragma mark -
+#pragma mark CelObjView
+CelObjView::CelObjView(const GuiResourceId viewId, const int16 loopNo, const int16 celNo) {
+ _info.type = kCelTypeView;
+ _info.resourceId = viewId;
+ _info.loopNo = loopNo;
+ _info.celNo = celNo;
+ _mirrorX = false;
+ _compressionType = kCelCompressionInvalid;
+ _transparent = true;
+
+ int cacheInsertIndex;
+ int cacheIndex = searchCache(_info, &cacheInsertIndex);
+ if (cacheIndex != -1) {
+ CelCacheEntry &entry = (*_cache)[cacheIndex];
+ *this = *dynamic_cast<CelObjView *>(entry.celObj);
+ entry.id = ++_nextCacheId;
+ return;
+ }
+
+ // TODO: The next code should be moved to a common file that
+ // generates view resource metadata for both SCI16 and SCI32
+ // implementations
+
+ Resource *resource = g_sci->getResMan()->findResource(ResourceId(kResourceTypeView, viewId), false);
+
+ // NOTE: SCI2.1/SQ6 just silently returns here.
+ if (!resource) {
+ warning("View resource %d not loaded", viewId);
+ return;
+ }
+
+ byte *data = resource->data;
+
+ _scaledWidth = READ_SCI11ENDIAN_UINT16(data + 14);
+ _scaledHeight = READ_SCI11ENDIAN_UINT16(data + 16);
+
+ if (_scaledWidth == 0 || _scaledHeight == 0) {
+ byte sizeFlag = data[5];
+ if (sizeFlag == 0) {
+ _scaledWidth = 320;
+ _scaledHeight = 200;
+ } else if (sizeFlag == 1) {
+ _scaledWidth = 640;
+ _scaledHeight = 480;
+ } else if (sizeFlag == 2) {
+ _scaledWidth = 640;
+ _scaledHeight = 400;
+ }
+ }
+
+ uint16 loopCount = data[2];
+ if (_info.loopNo >= loopCount) {
+ _info.loopNo = loopCount - 1;
+ }
+
+ // NOTE: This is the actual check, in the actual location,
+ // from SCI engine.
+ if (loopNo < 0) {
+ error("Loop is less than 0!");
+ }
+
+ const uint16 viewHeaderSize = READ_SCI11ENDIAN_UINT16(data);
+ const uint8 loopHeaderSize = data[12];
+ const uint8 viewHeaderFieldSize = 2;
+
+ byte *loopHeader = data + viewHeaderFieldSize + viewHeaderSize + (loopHeaderSize * _info.loopNo);
+
+ if ((int8)loopHeader[0] != -1) {
+ if (loopHeader[1] == 1) {
+ _mirrorX = true;
+ }
+
+ loopHeader = data + viewHeaderFieldSize + viewHeaderSize + (loopHeaderSize * (int8)loopHeader[0]);
+ }
+
+ uint8 celCount = loopHeader[2];
+ if (_info.celNo >= celCount) {
+ _info.celNo = celCount - 1;
+ }
+
+ _hunkPaletteOffset = READ_SCI11ENDIAN_UINT32(data + 8);
+ _celHeaderOffset = READ_SCI11ENDIAN_UINT32(loopHeader + 12) + (data[13] * _info.celNo);
+
+ byte *celHeader = data + _celHeaderOffset;
+
+ _width = READ_SCI11ENDIAN_UINT16(celHeader);
+ _height = READ_SCI11ENDIAN_UINT16(celHeader + 2);
+ _displace.x = _width / 2 - (int16)READ_SCI11ENDIAN_UINT16(celHeader + 4);
+ _displace.y = _height - (int16)READ_SCI11ENDIAN_UINT16(celHeader + 6) - 1;
+ _transparentColor = celHeader[8];
+ _compressionType = (CelCompressionType)celHeader[9];
+
+ if (_compressionType != kCelCompressionNone && _compressionType != kCelCompressionRLE) {
+ error("Compression type not supported - V: %d L: %d C: %d", _info.resourceId, _info.loopNo, _info.celNo);
+ }
+
+ if (celHeader[10] & 128) {
+ // NOTE: This is correct according to SCI2.1/SQ6/DOS;
+ // the engine re-reads the byte value as a word value
+ uint16 flags = READ_SCI11ENDIAN_UINT16(celHeader + 10);
+ _transparent = flags & 1 ? true : false;
+ _remap = flags & 2 ? true : false;
+ } else if (_compressionType == kCelCompressionNone) {
+ _remap = analyzeUncompressedForRemap();
+ } else {
+ _remap = analyzeForRemap();
+ }
+
+ putCopyInCache(cacheInsertIndex);
+}
+
+bool CelObjView::analyzeUncompressedForRemap() const {
+ byte *pixels = getResPointer() + READ_SCI11ENDIAN_UINT32(getResPointer() + _celHeaderOffset + 24);
+ for (int i = 0; i < _width * _height; ++i) {
+ byte pixel = pixels[i];
+ if (pixel >= g_sci->_gfxRemap32->getStartColor() && pixel <= g_sci->_gfxRemap32->getEndColor() && pixel != _transparentColor) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool CelObjView::analyzeForRemap() const {
+ READER_Compressed reader(*this, _width);
+ for (int y = 0; y < _height; y++) {
+ const byte *curRow = reader.getRow(y);
+ for (int x = 0; x < _width; x++) {
+ byte pixel = curRow[x];
+ if (pixel >= g_sci->_gfxRemap32->getStartColor() && pixel <= g_sci->_gfxRemap32->getEndColor() && pixel != _transparentColor) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+void CelObjView::draw(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition, bool mirrorX, const Ratio &scaleX, const Ratio &scaleY) {
+ _drawMirrored = mirrorX;
+ drawTo(target, targetRect, scaledPosition, scaleX, scaleY);
+}
+
+CelObjView *CelObjView::duplicate() const {
+ return new CelObjView(*this);
+}
+
+byte *CelObjView::getResPointer() const {
+ return g_sci->getResMan()->findResource(ResourceId(kResourceTypeView, _info.resourceId), false)->data;
+}
+
+#pragma mark -
+#pragma mark CelObjPic
+CelObjPic::CelObjPic(const GuiResourceId picId, const int16 celNo) {
+ _info.type = kCelTypePic;
+ _info.resourceId = picId;
+ _info.loopNo = 0;
+ _info.celNo = celNo;
+ _mirrorX = false;
+ _compressionType = kCelCompressionInvalid;
+ _transparent = true;
+ _remap = false;
+
+ int cacheInsertIndex;
+ int cacheIndex = searchCache(_info, &cacheInsertIndex);
+ if (cacheIndex != -1) {
+ CelCacheEntry &entry = (*_cache)[cacheIndex];
+ *this = *dynamic_cast<CelObjPic *>(entry.celObj);
+ entry.id = ++_nextCacheId;
+ return;
+ }
+
+ Resource *resource = g_sci->getResMan()->findResource(ResourceId(kResourceTypePic, picId), false);
+
+ // NOTE: SCI2.1/SQ6 just silently returns here.
+ if (!resource) {
+ warning("Pic resource %d not loaded", picId);
+ return;
+ }
+
+ byte *data = resource->data;
+
+ _celCount = data[2];
+
+ if (_info.celNo >= _celCount) {
+ error("Cel number %d greater than cel count %d", _info.celNo, _celCount);
+ }
+
+ _celHeaderOffset = READ_SCI11ENDIAN_UINT16(data) + (READ_SCI11ENDIAN_UINT16(data + 4) * _info.celNo);
+ _hunkPaletteOffset = READ_SCI11ENDIAN_UINT32(data + 6);
+
+ byte *celHeader = data + _celHeaderOffset;
+
+ _width = READ_SCI11ENDIAN_UINT16(celHeader);
+ _height = READ_SCI11ENDIAN_UINT16(celHeader + 2);
+ _displace.x = (int16)READ_SCI11ENDIAN_UINT16(celHeader + 4);
+ _displace.y = (int16)READ_SCI11ENDIAN_UINT16(celHeader + 6);
+ _transparentColor = celHeader[8];
+ _compressionType = (CelCompressionType)celHeader[9];
+ _priority = READ_SCI11ENDIAN_UINT16(celHeader + 36);
+ _relativePosition.x = (int16)READ_SCI11ENDIAN_UINT16(celHeader + 38);
+ _relativePosition.y = (int16)READ_SCI11ENDIAN_UINT16(celHeader + 40);
+
+ uint16 sizeFlag1 = READ_SCI11ENDIAN_UINT16(data + 10);
+ uint16 sizeFlag2 = READ_SCI11ENDIAN_UINT16(data + 12);
+
+ if (sizeFlag2) {
+ _scaledWidth = sizeFlag1;
+ _scaledHeight = sizeFlag2;
+ } else if (sizeFlag1 == 0) {
+ _scaledWidth = 320;
+ _scaledHeight = 200;
+ } else if (sizeFlag1 == 1) {
+ _scaledWidth = 640;
+ _scaledHeight = 480;
+ } else if (sizeFlag1 == 2) {
+ _scaledWidth = 640;
+ _scaledHeight = 400;
+ }
+
+ if (celHeader[10] & 128) {
+ // NOTE: This is correct according to SCI2.1/SQ6/DOS;
+ // the engine re-reads the byte value as a word value
+ uint16 flags = READ_SCI11ENDIAN_UINT16(celHeader + 10);
+ _transparent = flags & 1 ? true : false;
+ _remap = flags & 2 ? true : false;
+ } else {
+ _transparent = _compressionType != kCelCompressionNone ? true : analyzeUncompressedForSkip();
+
+ if (_compressionType != kCelCompressionNone && _compressionType != kCelCompressionRLE) {
+ error("Compression type not supported - P: %d C: %d", picId, celNo);
+ }
+ }
+
+ putCopyInCache(cacheInsertIndex);
+}
+
+bool CelObjPic::analyzeUncompressedForSkip() const {
+ byte *resource = getResPointer();
+ byte *pixels = resource + READ_SCI11ENDIAN_UINT32(resource + _celHeaderOffset + 24);
+ for (int i = 0; i < _width * _height; ++i) {
+ uint8 pixel = pixels[i];
+ if (pixel == _transparentColor) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void CelObjPic::draw(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition, const bool mirrorX) {
+ Ratio square;
+ _drawMirrored = mirrorX;
+ drawTo(target, targetRect, scaledPosition, square, square);
+}
+
+CelObjPic *CelObjPic::duplicate() const {
+ return new CelObjPic(*this);
+}
+
+byte *CelObjPic::getResPointer() const {
+ return g_sci->getResMan()->findResource(ResourceId(kResourceTypePic, _info.resourceId), false)->data;
+}
+
+#pragma mark -
+#pragma mark CelObjMem
+CelObjMem::CelObjMem(const reg_t bitmapObject) {
+ _info.type = kCelTypeMem;
+ _info.bitmap = bitmapObject;
+ _mirrorX = false;
+ _compressionType = kCelCompressionNone;
+ _celHeaderOffset = 0;
+ _transparent = true;
+
+ BitmapResource bitmap(bitmapObject);
+ _width = bitmap.getWidth();
+ _height = bitmap.getHeight();
+ _displace = bitmap.getDisplace();
+ _transparentColor = bitmap.getSkipColor();
+ _scaledWidth = bitmap.getScaledWidth();
+ _scaledHeight = bitmap.getScaledHeight();
+ _hunkPaletteOffset = bitmap.getHunkPaletteOffset();
+ _remap = bitmap.getRemap();
+}
+
+CelObjMem *CelObjMem::duplicate() const {
+ return new CelObjMem(*this);
+}
+
+byte *CelObjMem::getResPointer() const {
+ return g_sci->getEngineState()->_segMan->getHunkPointer(_info.bitmap);
+}
+
+#pragma mark -
+#pragma mark CelObjColor
+CelObjColor::CelObjColor(const uint8 color, const int16 width, const int16 height) {
+ _info.type = kCelTypeColor;
+ _info.color = color;
+ _displace.x = 0;
+ _displace.y = 0;
+ _scaledWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth;
+ _scaledHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight;
+ _hunkPaletteOffset = 0;
+ _mirrorX = false;
+ _remap = false;
+ _width = width;
+ _height = height;
+}
+
+void CelObjColor::draw(Buffer &target, const ScreenItem &screenItem, const Common::Rect &targetRect, const bool mirrorX) {
+ // TODO: The original engine sets this flag but why? One cannot
+ // draw a solid color mirrored.
+ _drawMirrored = mirrorX;
+ draw(target, targetRect);
+}
+void CelObjColor::draw(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition, bool mirrorX) {
+ error("Unsupported method");
+}
+void CelObjColor::draw(Buffer &target, const Common::Rect &targetRect) const {
+ target.fillRect(targetRect, _info.color);
+}
+
+CelObjColor *CelObjColor::duplicate() const {
+ return new CelObjColor(*this);
+}
+
+byte *CelObjColor::getResPointer() const {
+ error("Unsupported method");
+}
+} // End of namespace Sci
diff --git a/engines/sci/graphics/celobj32.h b/engines/sci/graphics/celobj32.h
new file mode 100644
index 0000000000..0bb4b03ae2
--- /dev/null
+++ b/engines/sci/graphics/celobj32.h
@@ -0,0 +1,581 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 SCI_GRAPHICS_CELOBJ32_H
+#define SCI_GRAPHICS_CELOBJ32_H
+
+#include "common/rational.h"
+#include "common/rect.h"
+#include "sci/resource.h"
+#include "sci/engine/vm_types.h"
+
+namespace Sci {
+typedef Common::Rational Ratio;
+
+enum CelType {
+ kCelTypeView = 0,
+ kCelTypePic = 1,
+ kCelTypeMem = 2,
+ kCelTypeColor = 3
+};
+
+enum CelCompressionType {
+ kCelCompressionNone = 0,
+ kCelCompressionRLE = 138,
+ kCelCompressionInvalid = 1000
+};
+
+/**
+ * A CelInfo32 object describes the basic properties of a
+ * cel object.
+ */
+struct CelInfo32 {
+ /**
+ * The type of the cel object.
+ */
+ CelType type;
+
+ /**
+ * For cel objects that draw from resources, the ID of
+ * the resource to load.
+ */
+ GuiResourceId resourceId;
+
+ /**
+ * For CelObjView, the loop number to draw from the
+ * view resource.
+ */
+ int16 loopNo;
+
+ /**
+ * For CelObjView and CelObjPic, the cel number to draw
+ * from the view or pic resource.
+ */
+ int16 celNo;
+
+ /**
+ * For CelObjMem, a segment register pointing to a heap
+ * resource containing headered bitmap data.
+ */
+ reg_t bitmap;
+
+ /**
+ * For CelObjColor, the fill color.
+ */
+ uint8 color;
+
+ // NOTE: In at least SCI2.1/SQ6, color is left
+ // uninitialised.
+ CelInfo32() :
+ type(kCelTypeMem),
+ resourceId(0),
+ loopNo(0),
+ celNo(0),
+ bitmap(NULL_REG) {}
+
+ // NOTE: This is the equivalence criteria used by
+ // CelObj::searchCache in at least SCI2.1/SQ6. Notably,
+ // it does not check the color field.
+ inline bool operator==(const CelInfo32 &other) {
+ return (
+ type == other.type &&
+ resourceId == other.resourceId &&
+ loopNo == other.loopNo &&
+ celNo == other.celNo &&
+ bitmap == other.bitmap
+ );
+ }
+
+ inline bool operator!=(const CelInfo32 &other) {
+ return !(*this == other);
+ }
+};
+
+class CelObj;
+struct CelCacheEntry {
+ /**
+ * A monotonically increasing cache ID used to identify
+ * the least recently used item in the cache for
+ * replacement.
+ */
+ int id;
+ CelObj *celObj;
+ CelCacheEntry() : id(0), celObj(nullptr) {}
+};
+
+typedef Common::Array<CelCacheEntry> CelCache;
+
+#pragma mark -
+#pragma mark CelScaler
+
+struct CelScalerTable {
+ /**
+ * A lookup table of indexes that should be used to find
+ * the correct column to read from the source bitmap
+ * when drawing a scaled version of the source bitmap.
+ */
+ int valuesX[1024];
+
+ /**
+ * The ratio used to generate the x-values.
+ */
+ Ratio scaleX;
+
+ /**
+ * A lookup table of indexes that should be used to find
+ * the correct row to read from a source bitmap when
+ * drawing a scaled version of the source bitmap.
+ */
+ int valuesY[1024];
+
+ /**
+ * The ratio used to generate the y-values.
+ */
+ Ratio scaleY;
+};
+
+class CelScaler {
+ /**
+ * Cached scale tables.
+ */
+ CelScalerTable _scaleTables[2];
+
+ /**
+ * The index of the most recently used scale table.
+ */
+ int _activeIndex;
+
+ /**
+ * Activates a scale table for the given X and Y ratios.
+ * If there is no table that matches the given ratios,
+ * the least most recently used table will be replaced
+ * and activated.
+ */
+ void activateScaleTables(const Ratio &scaleX, const Ratio &scaleY);
+
+ /**
+ * Builds a pixel lookup table in `table` for the given
+ * ratio. The table will be filled up to the specified
+ * size, which should be large enough to draw across the
+ * entire target buffer.
+ */
+ void buildLookupTable(int *table, const Ratio &ratio, const int size);
+
+public:
+ CelScaler() :
+ _scaleTables(),
+ _activeIndex(0) {
+ CelScalerTable &table = _scaleTables[0];
+ table.scaleX = Ratio();
+ table.scaleY = Ratio();
+ for (int i = 0; i < ARRAYSIZE(table.valuesX); ++i) {
+ table.valuesX[i] = i;
+ table.valuesY[i] = i;
+ }
+ for (int i = 1; i < ARRAYSIZE(_scaleTables); ++i) {
+ _scaleTables[i] = _scaleTables[0];
+ }
+ }
+
+ /**
+ * Retrieves scaler tables for the given X and Y ratios.
+ */
+ const CelScalerTable *getScalerTable(const Ratio &scaleX, const Ratio &scaleY);
+};
+
+#pragma mark -
+#pragma mark CelObj
+
+class ScreenItem;
+/**
+ * A cel object is the lowest-level rendering primitive in
+ * the SCI engine and draws itself directly to a target
+ * pixel buffer.
+ */
+class CelObj {
+protected:
+ /**
+ * When true, this cel will be horizontally mirrored
+ * when it is drawn. This is an internal flag that is
+ * set by draw methods based on the combination of the
+ * cel's `_mirrorX` property and the owner screen item's
+ * `_mirrorX` property.
+ */
+ bool _drawMirrored;
+
+public:
+ static CelScaler *_scaler;
+
+ /**
+ * The basic identifying information for this cel. This
+ * information effectively acts as a composite key for
+ * a cel object, and any cel object can be recreated
+ * from this data alone.
+ */
+ CelInfo32 _info;
+
+ /**
+ * The offset to the cel header for this cel within the
+ * raw resource data.
+ */
+ uint32 _celHeaderOffset;
+
+ /**
+ * The offset to the embedded palette for this cel
+ * within the raw resource data.
+ */
+ uint32 _hunkPaletteOffset;
+
+ /**
+ * The natural dimensions of the cel.
+ */
+ uint16 _width, _height;
+
+ /**
+ * TODO: Documentation
+ */
+ Common::Point _displace;
+
+ /**
+ * The dimensions of the original coordinate system for
+ * the cel. Used to scale cels from their native size
+ * to the correct size on screen.
+ *
+ * @note This is set to scriptWidth/Height for
+ * CelObjColor. For other cel objects, the value comes
+ * from the raw resource data. For text bitmaps, this is
+ * the width/height of the coordinate system used to
+ * generate the text, which also defaults to
+ * scriptWidth/Height but seems to typically be changed
+ * to more closely match the native screen resolution.
+ */
+ uint16 _scaledWidth, _scaledHeight;
+
+ /**
+ * The skip (transparent) color for the cel. When
+ * compositing, any pixels matching this color will not
+ * be copied to the buffer.
+ */
+ uint8 _transparentColor;
+
+ /**
+ * Whether or not this cel has any transparent regions.
+ * This is used for optimised drawing of non-transparent
+ * cels.
+ */
+ bool _transparent; // TODO: probably "skip"?
+
+ /**
+ * The compression type for the pixel data for this cel.
+ */
+ CelCompressionType _compressionType;
+
+ /**
+ * Whether or not this cel should be palette-remapped?
+ */
+ bool _remap;
+
+ /**
+ * If true, the cel contains pre-mirrored picture data.
+ * This value comes directly from the resource data and
+ * is XORed with the `_mirrorX` property of the owner
+ * screen item when rendering.
+ */
+ bool _mirrorX;
+
+ /**
+ * Initialises static CelObj members.
+ */
+ static void init();
+
+ /**
+ * Frees static CelObj members.
+ */
+ static void deinit();
+
+ virtual ~CelObj() {};
+
+ /**
+ * Draws the cel to the target buffer using the priority
+ * and positioning information from the given screen
+ * item. The mirroring of the cel will be unchanged from
+ * any previous call to draw.
+ */
+ void draw(Buffer &target, const ScreenItem &screenItem, const Common::Rect &targetRect) const;
+
+ /**
+ * Draws the cel to the target buffer using the priority
+ * and positioning information from the given screen
+ * item and the given mirror flag.
+ *
+ * @note In SCI engine, this function was a virtual
+ * function, but CelObjView, CelObjPic, and CelObjMem
+ * all used the same function and the compiler
+ * deduplicated the copies; we deduplicate the source by
+ * putting the implementation on CelObj instead of
+ * copying it to 3/4 of the subclasses.
+ */
+ virtual void draw(Buffer &target, const ScreenItem &screenItem, const Common::Rect &targetRect, const bool mirrorX);
+
+ /**
+ * Draws the cel to the target buffer using the
+ * positioning and mirroring information from the
+ * provided arguments.
+ *
+ * @note In SCI engine, this function was a virtual
+ * function, but CelObjView, CelObjPic, and CelObjMem
+ * all used the same function and the compiler
+ * deduplicated the copies; we deduplicate the source by
+ * putting the implementation on CelObj instead of
+ * copying it to 3/4 of the subclasses.
+ */
+ virtual void draw(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition, const bool mirrorX);
+
+ /**
+ * Draws the cel to the target buffer using the given
+ * position and scaling parameters. The mirroring of the
+ * cel will be unchanged from any previous call to draw.
+ */
+ void drawTo(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition, const Ratio &scaleX, const Ratio &scaleY) const;
+
+ /**
+ * Creates a copy of this cel on the free store and
+ * returns a pointer to the new object. The new cel will
+ * point to a shared copy of bitmap/resource data.
+ */
+ virtual CelObj *duplicate() const = 0;
+
+ /**
+ * Retrieves a pointer to the raw resource data for this
+ * cel. This method cannot be used with a CelObjColor.
+ */
+ virtual byte *getResPointer() const = 0;
+
+ /**
+ * Reads the pixel at the given coordinates. This method
+ * is valid only for CelObjView and CelObjPic.
+ */
+ virtual uint8 readPixel(uint16 x, uint16 y, bool mirrorX) const;
+
+ /**
+ * Submits the palette from this cel to the palette
+ * manager for integration into the master screen
+ * palette.
+ */
+ void submitPalette() const;
+
+#pragma mark -
+#pragma mark CelObj - Drawing
+private:
+ template<typename MAPPER, typename SCALER>
+ void render(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const;
+
+ template<typename MAPPER, typename SCALER>
+ void render(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition, const Ratio &scaleX, const Ratio &scaleY) const;
+
+ void drawHzFlip(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const;
+ void drawNoFlip(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const;
+ void drawUncompNoFlip(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const;
+ void drawUncompHzFlip(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const;
+ void scaleDraw(Buffer &target, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition) const;
+ void scaleDrawUncomp(Buffer &target, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition) const;
+
+ void drawHzFlipMap(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const;
+ void drawNoFlipMap(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const;
+ void drawUncompNoFlipMap(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const;
+ void drawUncompHzFlipMap(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const;
+ void scaleDrawMap(Buffer &target, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition) const;
+ void scaleDrawUncompMap(Buffer &target, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition) const;
+ // NOTE: The original includes versions of the above functions with priority parameters, which were not actually used in SCI32
+
+ void drawHzFlipNoMD(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const;
+ void drawNoFlipNoMD(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const;
+ void drawUncompNoFlipNoMD(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const;
+ void drawUncompNoFlipNoMDNoSkip(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const;
+ void drawUncompHzFlipNoMD(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const;
+ void drawUncompHzFlipNoMDNoSkip(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const;
+ void scaleDrawNoMD(Buffer &target, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition) const;
+ void scaleDrawUncompNoMD(Buffer &target, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition) const;
+ // NOTE: The original includes versions of the above functions with priority parameters, which were not actually used in SCI32
+
+#pragma mark -
+#pragma mark CelObj - Caching
+protected:
+ /**
+ * A monotonically increasing cache ID used to identify
+ * the least recently used item in the cache for
+ * replacement.
+ */
+ static int _nextCacheId;
+
+ /**
+ * A cache of cel objects used to avoid reinitialisation
+ * overhead for cels with the same CelInfo32.
+ */
+ // NOTE: At least SQ6 uses a fixed cache size of 100.
+ static CelCache *_cache;
+
+ /**
+ * Searches the cel cache for a CelObj matching the
+ * provided CelInfo32. If not found, -1 is returned.
+ * nextInsertIndex will receive the index of the oldest
+ * item in the cache, which can be used to replace
+ * the oldest item with a newer item.
+ */
+ int searchCache(const CelInfo32 &celInfo, int *nextInsertIndex) const;
+
+ /**
+ * Puts a copy of this CelObj into the cache at the
+ * given cache index.
+ */
+ void putCopyInCache(int index) const;
+};
+
+#pragma mark -
+#pragma mark CelObjView
+
+/**
+ * A CelObjView is the drawing primitive for a View type
+ * resource. Each CelObjView corresponds to a single cel
+ * within a single loop of a view.
+ */
+class CelObjView : public CelObj {
+private:
+ /**
+ * Analyses resources without baked-in remap flags
+ * to determine whether or not they should be remapped.
+ */
+ bool analyzeUncompressedForRemap() const;
+
+ /**
+ * Analyses compressed resources without baked-in remap
+ * flags to determine whether or not they should be
+ * remapped.
+ */
+ bool analyzeForRemap() const;
+
+public:
+ CelObjView(GuiResourceId viewId, int16 loopNo, int16 celNo);
+ virtual ~CelObjView() override {};
+
+ using CelObj::draw;
+
+ /**
+ * Draws the cel to the target buffer using the
+ * positioning, mirroring, and scaling information from
+ * the provided arguments.
+ */
+ void draw(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition, bool mirrorX, const Ratio &scaleX, const Ratio &scaleY);
+
+ virtual CelObjView *duplicate() const override;
+ virtual byte *getResPointer() const override;
+};
+
+#pragma mark -
+#pragma mark CelObjPic
+
+/**
+ * A CelObjPic is the drawing primitive for a Picture type
+ * resource. Each CelObjPic corresponds to a single cel
+ * within a picture.
+ */
+class CelObjPic : public CelObj {
+private:
+ /**
+ * Analyses uncompressed resources without baked-in skip
+ * flags to determine whether or not they can use fast
+ * blitting.
+ */
+ bool analyzeUncompressedForSkip() const;
+
+public:
+ /**
+ * The number of cels in the original picture resource.
+ */
+ uint8 _celCount;
+
+ /**
+ * The position of this cel relative to the top-left
+ * corner of the picture.
+ */
+ Common::Point _relativePosition;
+
+ /**
+ * The z-buffer priority for this cel. Higher prorities
+ * are drawn on top of lower priorities.
+ */
+ int16 _priority;
+
+ CelObjPic(GuiResourceId pictureId, int16 celNo);
+ virtual ~CelObjPic() override {};
+
+ using CelObj::draw;
+ virtual void draw(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition, const bool mirrorX) override;
+
+ virtual CelObjPic *duplicate() const override;
+ virtual byte *getResPointer() const override;
+};
+
+#pragma mark -
+#pragma mark CelObjMem
+
+/**
+ * A CelObjMem is the drawing primitive for arbitrary
+ * bitmaps generated in memory. Generated bitmaps in SCI32
+ * include text & vector drawings and per-pixel screen
+ * transitions like dissolves.
+ */
+class CelObjMem : public CelObj {
+public:
+ CelObjMem(reg_t bitmap);
+ virtual ~CelObjMem() override {};
+
+ virtual CelObjMem *duplicate() const override;
+ virtual byte *getResPointer() const override;
+};
+
+#pragma mark -
+#pragma mark CelObjColor
+
+/**
+ * A CelObjColor is the drawing primitive for fast,
+ * low-memory, flat color fills.
+ */
+class CelObjColor : public CelObj {
+public:
+ CelObjColor(uint8 color, int16 width, int16 height);
+ virtual ~CelObjColor() override {};
+
+ using CelObj::draw;
+ /**
+ * Block fills the target buffer with the cel color.
+ */
+ void draw(Buffer &target, const Common::Rect &targetRect) const;
+ virtual void draw(Buffer &target, const ScreenItem &screenItem, const Common::Rect &targetRect, const bool mirrorX) override;
+ virtual void draw(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition, const bool mirrorX) override;
+
+ virtual CelObjColor *duplicate() const override;
+ virtual byte *getResPointer() const override;
+};
+} // End of namespace Sci
+
+#endif
diff --git a/engines/sci/graphics/compare.cpp b/engines/sci/graphics/compare.cpp
index 716a366b7c..729eeeaf81 100644
--- a/engines/sci/graphics/compare.cpp
+++ b/engines/sci/graphics/compare.cpp
@@ -67,7 +67,7 @@ uint16 GfxCompare::isOnControl(uint16 screenMask, const Common::Rect &rect) {
return result;
}
-reg_t GfxCompare::canBeHereCheckRectList(reg_t checkObject, const Common::Rect &checkRect, List *list) {
+reg_t GfxCompare::canBeHereCheckRectList(const reg_t checkObject, const Common::Rect &checkRect, const List *list, const uint16 signalFlags) const {
reg_t curAddress = list->first;
Node *curNode = _segMan->lookupNode(curAddress);
reg_t curObject;
@@ -78,7 +78,7 @@ reg_t GfxCompare::canBeHereCheckRectList(reg_t checkObject, const Common::Rect &
curObject = curNode->value;
if (curObject != checkObject) {
signal = readSelectorValue(_segMan, curObject, SELECTOR(signal));
- if (!(signal & (kSignalIgnoreActor | kSignalRemoveView | kSignalNoUpdate))) {
+ if (!(signal & signalFlags)) {
curRect.left = readSelectorValue(_segMan, curObject, SELECTOR(brLeft));
curRect.top = readSelectorValue(_segMan, curObject, SELECTOR(brTop));
curRect.right = readSelectorValue(_segMan, curObject, SELECTOR(brRight));
@@ -112,11 +112,6 @@ void GfxCompare::kernelSetNowSeen(reg_t objectReference) {
GfxView *view = NULL;
Common::Rect celRect(0, 0);
GuiResourceId viewId = (GuiResourceId)readSelectorValue(_segMan, objectReference, SELECTOR(view));
-
- // HACK: Ignore invalid views for now (perhaps unimplemented text views?)
- if (viewId == 0xFFFF) // invalid view
- return;
-
int16 loopNo = readSelectorValue(_segMan, objectReference, SELECTOR(loop));
int16 celNo = readSelectorValue(_segMan, objectReference, SELECTOR(cel));
int16 x = (int16)readSelectorValue(_segMan, objectReference, SELECTOR(x));
@@ -126,26 +121,8 @@ void GfxCompare::kernelSetNowSeen(reg_t objectReference) {
z = (int16)readSelectorValue(_segMan, objectReference, SELECTOR(z));
view = _cache->getView(viewId);
-
-#ifdef ENABLE_SCI32
- if (view->isSci2Hires())
- view->adjustToUpscaledCoordinates(y, x);
- else if ((getSciVersion() >= SCI_VERSION_2_1_EARLY) && (getSciVersion() <= SCI_VERSION_2_1_LATE))
- _coordAdjuster->fromScriptToDisplay(y, x);
-#endif
-
view->getCelRect(loopNo, celNo, x, y, z, celRect);
-#ifdef ENABLE_SCI32
- if (view->isSci2Hires()) {
- view->adjustBackUpscaledCoordinates(celRect.top, celRect.left);
- view->adjustBackUpscaledCoordinates(celRect.bottom, celRect.right);
- } else if ((getSciVersion() >= SCI_VERSION_2_1_EARLY) && (getSciVersion() <= SCI_VERSION_2_1_LATE)) {
- _coordAdjuster->fromDisplayToScript(celRect.top, celRect.left);
- _coordAdjuster->fromDisplayToScript(celRect.bottom, celRect.right);
- }
-#endif
-
if (lookupSelector(_segMan, objectReference, SELECTOR(nsTop), NULL, NULL) == kSelectorVariable) {
setNSRect(objectReference, celRect);
}
@@ -153,32 +130,57 @@ void GfxCompare::kernelSetNowSeen(reg_t objectReference) {
reg_t GfxCompare::kernelCanBeHere(reg_t curObject, reg_t listReference) {
Common::Rect checkRect;
- Common::Rect adjustedRect;
- uint16 signal, controlMask;
uint16 result;
checkRect.left = readSelectorValue(_segMan, curObject, SELECTOR(brLeft));
checkRect.top = readSelectorValue(_segMan, curObject, SELECTOR(brTop));
checkRect.right = readSelectorValue(_segMan, curObject, SELECTOR(brRight));
checkRect.bottom = readSelectorValue(_segMan, curObject, SELECTOR(brBottom));
+ uint16 signal = readSelectorValue(_segMan, curObject, SELECTOR(signal));
if (!checkRect.isValidRect()) { // can occur in Iceman and Mother Goose - HACK? TODO: is this really occuring in sierra sci? check this
warning("kCan(t)BeHere - invalid rect %d, %d -> %d, %d", checkRect.left, checkRect.top, checkRect.right, checkRect.bottom);
return NULL_REG; // this means "can be here"
}
- adjustedRect = _coordAdjuster->onControl(checkRect);
-
- signal = readSelectorValue(_segMan, curObject, SELECTOR(signal));
- controlMask = readSelectorValue(_segMan, curObject, SELECTOR(illegalBits));
+ Common::Rect adjustedRect = _coordAdjuster->onControl(checkRect);
+ uint16 controlMask = readSelectorValue(_segMan, curObject, SELECTOR(illegalBits));
result = isOnControl(GFX_SCREEN_MASK_CONTROL, adjustedRect) & controlMask;
if ((!result) && (signal & (kSignalIgnoreActor | kSignalRemoveView)) == 0) {
List *list = _segMan->lookupList(listReference);
if (!list)
error("kCanBeHere called with non-list as parameter");
- return canBeHereCheckRectList(curObject, checkRect, list);
+ return canBeHereCheckRectList(curObject, checkRect, list, kSignalIgnoreActor | kSignalRemoveView | kSignalNoUpdate);
}
+
+ return make_reg(0, result);
+}
+
+reg_t GfxCompare::kernelCantBeHere32(const reg_t curObject, const reg_t listReference) const {
+ // Most of SCI32 graphics code converts rects from the VM to exclusive
+ // rects before operating on them, but this call leverages SCI16 engine
+ // code that operates on inclusive rects, so the rect's bottom-right
+ // point is not modified like in other SCI32 kernel calls
+ Common::Rect checkRect(
+ readSelectorValue(_segMan, curObject, SELECTOR(brLeft)),
+ readSelectorValue(_segMan, curObject, SELECTOR(brTop)),
+ readSelectorValue(_segMan, curObject, SELECTOR(brRight)),
+ readSelectorValue(_segMan, curObject, SELECTOR(brBottom))
+ );
+
+ uint16 result = 0;
+ uint16 signal = readSelectorValue(_segMan, curObject, SELECTOR(signal));
+ const uint16 signalFlags = kSignalIgnoreActor | kSignalHidden;
+
+ if ((signal & signalFlags) == 0) {
+ List *list = _segMan->lookupList(listReference);
+ if (!list) {
+ error("kCantBeHere called with non-list as parameter");
+ }
+ result = !canBeHereCheckRectList(curObject, checkRect, list, signalFlags).isNull();
+ }
+
return make_reg(0, result);
}
@@ -201,15 +203,9 @@ void GfxCompare::kernelBaseSetter(reg_t object) {
GuiResourceId viewId = readSelectorValue(_segMan, object, SELECTOR(view));
int16 loopNo = readSelectorValue(_segMan, object, SELECTOR(loop));
int16 celNo = readSelectorValue(_segMan, object, SELECTOR(cel));
-
- // HACK: Ignore invalid views for now (perhaps unimplemented text views?)
- if (viewId == 0xFFFF) // invalid view
- return;
-
uint16 scaleSignal = 0;
- if (getSciVersion() >= SCI_VERSION_1_1) {
+ if (getSciVersion() >= SCI_VERSION_1_1)
scaleSignal = readSelectorValue(_segMan, object, SELECTOR(scaleSignal));
- }
Common::Rect celRect;
diff --git a/engines/sci/graphics/compare.h b/engines/sci/graphics/compare.h
index 88b44aeeb1..c7005980d0 100644
--- a/engines/sci/graphics/compare.h
+++ b/engines/sci/graphics/compare.h
@@ -40,6 +40,7 @@ public:
uint16 kernelOnControl(byte screenMask, const Common::Rect &rect);
void kernelSetNowSeen(reg_t objectReference);
reg_t kernelCanBeHere(reg_t curObject, reg_t listReference);
+ reg_t kernelCantBeHere32(const reg_t curObject, const reg_t listReference) const;
bool kernelIsItSkip(GuiResourceId viewId, int16 loopNo, int16 celNo, Common::Point position);
void kernelBaseSetter(reg_t object);
Common::Rect getNSRect(reg_t object);
@@ -58,7 +59,7 @@ private:
* *different* from checkObject, has a brRect which is contained inside
* checkRect.
*/
- reg_t canBeHereCheckRectList(reg_t checkObject, const Common::Rect &checkRect, List *list);
+ reg_t canBeHereCheckRectList(const reg_t checkObject, const Common::Rect &checkRect, const List *list, const uint16 signalFlags) const;
};
} // End of namespace Sci
diff --git a/engines/sci/graphics/controls16.cpp b/engines/sci/graphics/controls16.cpp
index e2e250cf9d..b4bd92699a 100644
--- a/engines/sci/graphics/controls16.cpp
+++ b/engines/sci/graphics/controls16.cpp
@@ -151,7 +151,7 @@ void GfxControls16::kernelTexteditChange(reg_t controlObject, reg_t eventObject)
Common::Rect rect;
if (textReference.isNull())
- error("kEditControl called on object that doesnt have a text reference");
+ error("kEditControl called on object that doesn't have a text reference");
text = _segMan->getString(textReference);
uint16 oldCursorPos = cursorPos;
diff --git a/engines/sci/graphics/controls32.cpp b/engines/sci/graphics/controls32.cpp
index 1bd497ce98..a877d8c276 100644
--- a/engines/sci/graphics/controls32.cpp
+++ b/engines/sci/graphics/controls32.cpp
@@ -23,9 +23,11 @@
#include "common/system.h"
#include "sci/sci.h"
+#include "sci/console.h"
#include "sci/event.h"
#include "sci/engine/kernel.h"
#include "sci/engine/seg_manager.h"
+#include "sci/engine/state.h"
#include "sci/graphics/cache.h"
#include "sci/graphics/compare.h"
#include "sci/graphics/controls32.h"
@@ -34,171 +36,321 @@
#include "sci/graphics/text32.h"
namespace Sci {
+GfxControls32::GfxControls32(SegManager *segMan, GfxCache *cache, GfxText32 *text) :
+ _segMan(segMan),
+ _gfxCache(cache),
+ _gfxText32(text),
+ _overwriteMode(false),
+ _nextCursorFlashTick(0) {}
-GfxControls32::GfxControls32(SegManager *segMan, GfxCache *cache, GfxText32 *text)
- : _segMan(segMan), _cache(cache), _text(text) {
-}
+reg_t GfxControls32::kernelEditText(const reg_t controlObject) {
+ SegManager *segMan = _segMan;
-GfxControls32::~GfxControls32() {
-}
+ TextEditor editor;
+ reg_t textObject = readSelector(_segMan, controlObject, SELECTOR(text));
+ editor.text = _segMan->getString(textObject);
+ editor.foreColor = readSelectorValue(_segMan, controlObject, SELECTOR(fore));
+ editor.backColor = readSelectorValue(_segMan, controlObject, SELECTOR(back));
+ editor.skipColor = readSelectorValue(_segMan, controlObject, SELECTOR(skip));
+ editor.fontId = readSelectorValue(_segMan, controlObject, SELECTOR(font));
+ editor.maxLength = readSelectorValue(_segMan, controlObject, SELECTOR(width));
+ editor.bitmap = readSelector(_segMan, controlObject, SELECTOR(bitmap));
+ editor.cursorCharPosition = 0;
+ editor.cursorIsDrawn = false;
+ editor.borderColor = readSelectorValue(_segMan, controlObject, SELECTOR(borderColor));
-void GfxControls32::kernelTexteditChange(reg_t controlObject) {
- SciEvent curEvent;
- uint16 maxChars = 40; //readSelectorValue(_segMan, controlObject, SELECTOR(max)); // TODO
- reg_t textReference = readSelector(_segMan, controlObject, SELECTOR(text));
- GfxFont *font = _cache->getFont(readSelectorValue(_segMan, controlObject, SELECTOR(font)));
- Common::String text;
- uint16 textSize;
- bool textChanged = false;
- bool textAddChar = false;
- Common::Rect rect;
+ reg_t titleObject = readSelector(_segMan, controlObject, SELECTOR(title));
+
+ int16 titleHeight = 0;
+ GuiResourceId titleFontId = readSelectorValue(_segMan, controlObject, SELECTOR(titleFont));
+ if (!titleObject.isNull()) {
+ GfxFont *titleFont = _gfxCache->getFont(titleFontId);
+ titleHeight += _gfxText32->scaleUpHeight(titleFont->getHeight()) + 1;
+ if (editor.borderColor != -1) {
+ titleHeight += 2;
+ }
+ }
- if (textReference.isNull())
- error("kEditControl called on object that doesnt have a text reference");
- text = _segMan->getString(textReference);
+ int16 width = 0;
+ int16 height = titleHeight;
- // TODO: Finish this
- warning("kEditText ('%s')", text.c_str());
- return;
+ GfxFont *editorFont = _gfxCache->getFont(editor.fontId);
+ height += _gfxText32->scaleUpHeight(editorFont->getHeight()) + 1;
+ _gfxText32->setFont(editor.fontId);
+ int16 emSize = _gfxText32->getCharWidth('M', true);
+ width += editor.maxLength * emSize + 1;
+ if (editor.borderColor != -1) {
+ width += 4;
+ height += 2;
+ }
- uint16 cursorPos = 0;
- //uint16 oldCursorPos = cursorPos;
- bool captureEvents = true;
- EventManager* eventMan = g_sci->getEventManager();
+ Common::Rect editorPlaneRect(width, height);
+ editorPlaneRect.translate(readSelectorValue(_segMan, controlObject, SELECTOR(x)), readSelectorValue(_segMan, controlObject, SELECTOR(y)));
- while (captureEvents) {
- curEvent = g_sci->getEventManager()->getSciEvent(SCI_EVENT_KEYBOARD | SCI_EVENT_PEEK);
+ reg_t planeObj = readSelector(_segMan, controlObject, SELECTOR(plane));
+ Plane *sourcePlane = g_sci->_gfxFrameout->getVisiblePlanes().findByObject(planeObj);
+ if (sourcePlane == nullptr) {
+ error("Could not find plane %04x:%04x", PRINT_REG(planeObj));
+ }
+ editorPlaneRect.translate(sourcePlane->_gameRect.left, sourcePlane->_gameRect.top);
- if (curEvent.type == SCI_EVENT_NONE) {
- eventMan->getSciEvent(SCI_EVENT_KEYBOARD); // consume the event
+ editor.textRect = Common::Rect(2, titleHeight + 2, width - 1, height - 1);
+ editor.width = width;
+
+ if (editor.bitmap.isNull()) {
+ TextAlign alignment = (TextAlign)readSelectorValue(_segMan, controlObject, SELECTOR(mode));
+
+ if (titleObject.isNull()) {
+ bool dimmed = readSelectorValue(_segMan, controlObject, SELECTOR(dimmed));
+ editor.bitmap = _gfxText32->createFontBitmap(width, height, editor.textRect, editor.text, editor.foreColor, editor.backColor, editor.skipColor, editor.fontId, alignment, editor.borderColor, dimmed, true);
} else {
- textSize = text.size();
+ Common::String title = _segMan->getString(titleObject);
+ int16 titleBackColor = readSelectorValue(_segMan, controlObject, SELECTOR(titleBack));
+ int16 titleForeColor = readSelectorValue(_segMan, controlObject, SELECTOR(titleFore));
+ editor.bitmap = _gfxText32->createTitledBitmap(width, height, editor.textRect, editor.text, editor.foreColor, editor.backColor, editor.skipColor, editor.fontId, alignment, editor.borderColor, title, titleForeColor, titleBackColor, titleFontId, true);
+ }
+ }
+
+ drawCursor(editor);
- switch (curEvent.type) {
- case SCI_EVENT_MOUSE_PRESS:
- // TODO: Implement mouse support for cursor change
+ Plane *plane = new Plane(editorPlaneRect, kPlanePicTransparent);
+ plane->changePic();
+ g_sci->_gfxFrameout->addPlane(*plane);
+
+ CelInfo32 celInfo;
+ celInfo.type = kCelTypeMem;
+ celInfo.bitmap = editor.bitmap;
+
+ ScreenItem *screenItem = new ScreenItem(plane->_object, celInfo, Common::Point(), ScaleInfo());
+ plane->_screenItemList.add(screenItem);
+
+ // frameOut must be called after the screen item is
+ // created, and before it is updated at the end of the
+ // event loop, otherwise it has both created and updated
+ // flags set which crashes the engine (it runs updates
+ // before creations)
+ g_sci->_gfxFrameout->frameOut(true);
+
+ EventManager *eventManager = g_sci->getEventManager();
+ bool clearTextOnInput = true;
+ bool textChanged = false;
+ for (;;) {
+ // We peek here because the last event needs to be allowed to
+ // dispatch a second time to the normal event handling system.
+ // In the actual engine, the event is always consumed and then
+ // the last event just gets posted back to the event manager for
+ // reprocessing, but instead, we only remove the event from the
+ // queue *after* we have determined it is not a defocusing event
+ const SciEvent event = eventManager->getSciEvent(SCI_EVENT_ANY | SCI_EVENT_PEEK);
+
+ bool focused = true;
+ // Original engine did not have a QUIT event but we have to handle it
+ if (event.type == SCI_EVENT_QUIT) {
+ focused = false;
+ break;
+ } else if (event.type == SCI_EVENT_MOUSE_PRESS && !editorPlaneRect.contains(event.mousePosSci)) {
+ focused = false;
+ } else if (event.type == SCI_EVENT_KEYBOARD) {
+ switch (event.character) {
+ case SCI_KEY_ESC:
+ case SCI_KEY_UP:
+ case SCI_KEY_DOWN:
+ case SCI_KEY_TAB:
+ case SCI_KEY_SHIFT_TAB:
+ case SCI_KEY_ENTER:
+ focused = false;
break;
- case SCI_EVENT_KEYBOARD:
- switch (curEvent.character) {
- case SCI_KEY_BACKSPACE:
- if (cursorPos > 0) {
- cursorPos--; text.deleteChar(cursorPos);
- textChanged = true;
- }
- eventMan->getSciEvent(SCI_EVENT_KEYBOARD); // consume the event
- break;
- case SCI_KEY_DELETE:
- if (cursorPos < textSize) {
- text.deleteChar(cursorPos);
- textChanged = true;
- }
- eventMan->getSciEvent(SCI_EVENT_KEYBOARD); // consume the event
- break;
- case SCI_KEY_HOME: // HOME
- cursorPos = 0; textChanged = true;
- eventMan->getSciEvent(SCI_EVENT_KEYBOARD); // consume the event
- break;
- case SCI_KEY_END: // END
- cursorPos = textSize; textChanged = true;
- eventMan->getSciEvent(SCI_EVENT_KEYBOARD); // consume the event
- break;
- case SCI_KEY_LEFT: // LEFT
- if (cursorPos > 0) {
- cursorPos--; textChanged = true;
- }
- eventMan->getSciEvent(SCI_EVENT_KEYBOARD); // consume the event
- break;
- case SCI_KEY_RIGHT: // RIGHT
- if (cursorPos + 1 <= textSize) {
- cursorPos++; textChanged = true;
- }
- eventMan->getSciEvent(SCI_EVENT_KEYBOARD); // consume the event
- break;
- case 3: // returned in SCI1 late and newer when Control - C is pressed
- if (curEvent.modifiers & SCI_KEYMOD_CTRL) {
- // Control-C erases the whole line
- cursorPos = 0; text.clear();
- textChanged = true;
+ }
+ }
+
+ if (!focused) {
+ break;
+ }
+
+ // Consume the event now that we know it is not one of the
+ // defocusing events above
+ eventManager->getSciEvent(SCI_EVENT_ANY);
+
+ // NOTE: In the original engine, the font and bitmap were
+ // reset here on each iteration through the loop, but it
+ // doesn't seem like this should be necessary since
+ // control is not yielded back to the VM until input is
+ // received, which means there is nothing that could modify
+ // the GfxText32's state with a different font in the
+ // meantime
+
+ bool shouldDeleteChar = false;
+ bool shouldRedrawText = false;
+ uint16 lastCursorPosition = editor.cursorCharPosition;
+ if (event.type == SCI_EVENT_KEYBOARD) {
+ switch (event.character) {
+ case SCI_KEY_LEFT:
+ clearTextOnInput = false;
+ if (editor.cursorCharPosition > 0) {
+ --editor.cursorCharPosition;
+ }
+ break;
+
+ case SCI_KEY_RIGHT:
+ clearTextOnInput = false;
+ if (editor.cursorCharPosition < editor.text.size()) {
+ ++editor.cursorCharPosition;
+ }
+ break;
+
+ case SCI_KEY_HOME:
+ clearTextOnInput = false;
+ editor.cursorCharPosition = 0;
+ break;
+
+ case SCI_KEY_END:
+ clearTextOnInput = false;
+ editor.cursorCharPosition = editor.text.size();
+ break;
+
+ case SCI_KEY_INSERT:
+ clearTextOnInput = false;
+ // Redrawing also changes the cursor rect to
+ // reflect the new insertion mode
+ shouldRedrawText = true;
+ _overwriteMode = !_overwriteMode;
+ break;
+
+ case SCI_KEY_DELETE:
+ clearTextOnInput = false;
+ if (editor.cursorCharPosition < editor.text.size()) {
+ shouldDeleteChar = true;
+ }
+ break;
+
+ case SCI_KEY_BACKSPACE:
+ clearTextOnInput = false;
+ shouldDeleteChar = true;
+ if (editor.cursorCharPosition > 0) {
+ --editor.cursorCharPosition;
+ }
+ break;
+
+ case SCI_KEY_ETX:
+ editor.text.clear();
+ editor.cursorCharPosition = 0;
+ shouldRedrawText = true;
+ break;
+
+ default: {
+ if (event.character >= 20 && event.character < 257) {
+ if (clearTextOnInput) {
+ clearTextOnInput = false;
+ editor.text.clear();
}
- eventMan->getSciEvent(SCI_EVENT_KEYBOARD); // consume the event
- break;
- case SCI_KEY_UP:
- case SCI_KEY_DOWN:
- case SCI_KEY_ENTER:
- case SCI_KEY_ESC:
- case SCI_KEY_TAB:
- case SCI_KEY_SHIFT_TAB:
- captureEvents = false;
- break;
- default:
- if ((curEvent.modifiers & SCI_KEYMOD_CTRL) && curEvent.character == 'c') {
- // Control-C in earlier SCI games (SCI0 - SCI1 middle)
- // Control-C erases the whole line
- cursorPos = 0; text.clear();
- textChanged = true;
- } else if (curEvent.character > 31 && curEvent.character < 256 && textSize < maxChars) {
- // insert pressed character
- textAddChar = true;
- textChanged = true;
+
+ if (
+ (_overwriteMode && editor.cursorCharPosition < editor.maxLength) ||
+ (editor.text.size() < editor.maxLength && _gfxText32->getCharWidth(event.character, true) + _gfxText32->getStringWidth(editor.text) < editor.textRect.width())
+ ) {
+ if (_overwriteMode && editor.cursorCharPosition < editor.text.size()) {
+ editor.text.setChar(event.character, editor.cursorCharPosition);
+ } else {
+ editor.text.insertChar(event.character, editor.cursorCharPosition);
+ }
+
+ ++editor.cursorCharPosition;
+ shouldRedrawText = true;
}
- eventMan->getSciEvent(SCI_EVENT_KEYBOARD); // consume the event
- break;
}
- break;
+ }
}
}
- if (textChanged) {
- rect = g_sci->_gfxCompare->getNSRect(controlObject);
+ if (shouldDeleteChar) {
+ shouldRedrawText = true;
+ if (editor.cursorCharPosition < editor.text.size()) {
+ editor.text.deleteChar(editor.cursorCharPosition);
+ }
+ }
- if (textAddChar) {
- const char *textPtr = text.c_str();
+ if (shouldRedrawText) {
+ eraseCursor(editor);
+ _gfxText32->erase(editor.textRect, true);
+ _gfxText32->drawTextBox(editor.text);
+ drawCursor(editor);
+ textChanged = true;
+ screenItem->_updated = g_sci->_gfxFrameout->getScreenCount();
+ } else if (editor.cursorCharPosition != lastCursorPosition) {
+ eraseCursor(editor);
+ drawCursor(editor);
+ screenItem->_updated = g_sci->_gfxFrameout->getScreenCount();
+ } else {
+ flashCursor(editor);
+ screenItem->_updated = g_sci->_gfxFrameout->getScreenCount();
+ }
- // We check if we are really able to add the new char
- uint16 textWidth = 0;
- while (*textPtr)
- textWidth += font->getCharWidth((byte)*textPtr++);
- textWidth += font->getCharWidth(curEvent.character);
+ g_sci->_gfxFrameout->frameOut(true);
+ g_sci->getSciDebugger()->onFrame();
+ g_sci->getEngineState()->speedThrottler(16);
+ g_sci->getEngineState()->_throttleTrigger = true;
+ }
- // Does it fit?
- if (textWidth >= rect.width()) {
- return;
- }
+ g_sci->_gfxFrameout->deletePlane(*plane);
+ if (readSelectorValue(segMan, controlObject, SELECTOR(frameOut))) {
+ g_sci->_gfxFrameout->frameOut(true);
+ }
- text.insertChar(curEvent.character, cursorPos++);
+ _segMan->freeHunkEntry(editor.bitmap);
- // Note: the following checkAltInput call might make the text
- // too wide to fit, but SSCI fails to check that too.
- }
+ if (textChanged) {
+ editor.text.trim();
+ SciString *string = _segMan->lookupString(textObject);
+ string->fromString(editor.text);
+ }
+
+ return make_reg(0, textChanged);
+}
- reg_t hunkId = readSelector(_segMan, controlObject, SELECTOR(bitmap));
- Common::Rect nsRect = g_sci->_gfxCompare->getNSRect(controlObject);
- //texteditCursorErase(); // TODO: Cursor
+void GfxControls32::drawCursor(TextEditor &editor) {
+ if (!editor.cursorIsDrawn) {
+ editor.cursorRect.left = editor.textRect.left + _gfxText32->getTextWidth(editor.text, 0, editor.cursorCharPosition);
- // Write back string
- _segMan->strcpy(textReference, text.c_str());
- // Modify the buffer and show it
- _text->createTextBitmap(controlObject, 0, 0, hunkId);
+ const int16 scaledFontHeight = _gfxText32->scaleUpHeight(_gfxText32->_font->getHeight());
- _text->drawTextBitmap(0, 0, nsRect, controlObject);
- //texteditCursorDraw(rect, text.c_str(), cursorPos); // TODO: Cursor
- g_system->updateScreen();
+ // NOTE: The original code branched on borderColor here but
+ // the two branches appeared to be identical, differing only
+ // because the compiler decided to be differently clever
+ // when optimising multiplication in each branch
+ if (_overwriteMode) {
+ editor.cursorRect.top = editor.textRect.top;
+ editor.cursorRect.setHeight(scaledFontHeight);
} else {
- // TODO: Cursor
- /*
- if (g_system->getMillis() >= _texteditBlinkTime) {
- _paint16->invertRect(_texteditCursorRect);
- _paint16->bitsShow(_texteditCursorRect);
- _texteditCursorVisible = !_texteditCursorVisible;
- texteditSetBlinkTime();
- }
- */
+ editor.cursorRect.top = editor.textRect.top + scaledFontHeight - 1;
+ editor.cursorRect.setHeight(1);
}
- textAddChar = false;
- textChanged = false;
- g_sci->sleep(10);
- } // while
+ const char currentChar = editor.cursorCharPosition < editor.text.size() ? editor.text[editor.cursorCharPosition] : ' ';
+ editor.cursorRect.setWidth(_gfxText32->getCharWidth(currentChar, true));
+
+ _gfxText32->invertRect(editor.bitmap, editor.width, editor.cursorRect, editor.foreColor, editor.backColor, true);
+
+ editor.cursorIsDrawn = true;
+ }
+
+ _nextCursorFlashTick = g_sci->getTickCount() + 30;
+}
+
+void GfxControls32::eraseCursor(TextEditor &editor) {
+ if (editor.cursorIsDrawn) {
+ _gfxText32->invertRect(editor.bitmap, editor.width, editor.cursorRect, editor.foreColor, editor.backColor, true);
+ editor.cursorIsDrawn = false;
+ }
+
+ _nextCursorFlashTick = g_sci->getTickCount() + 30;
}
+void GfxControls32::flashCursor(TextEditor &editor) {
+ if (g_sci->getTickCount() > _nextCursorFlashTick) {
+ _gfxText32->invertRect(editor.bitmap, editor.width, editor.cursorRect, editor.foreColor, editor.backColor, true);
+
+ editor.cursorIsDrawn = !editor.cursorIsDrawn;
+ _nextCursorFlashTick = g_sci->getTickCount() + 30;
+ }
+}
} // End of namespace Sci
diff --git a/engines/sci/graphics/controls32.h b/engines/sci/graphics/controls32.h
index 5af7c20f16..1bb7679ddd 100644
--- a/engines/sci/graphics/controls32.h
+++ b/engines/sci/graphics/controls32.h
@@ -29,20 +29,95 @@ class GfxCache;
class GfxScreen;
class GfxText32;
+struct TextEditor {
+ /**
+ * The bitmap where the editor is rendered.
+ */
+ reg_t bitmap;
+
+ /**
+ * The width of the editor, in bitmap pixels.
+ */
+ int16 width;
+
+ /**
+ * The text in the editor.
+ */
+ Common::String text;
+
+ /**
+ * The rect where text should be drawn into the editor,
+ * in bitmap pixels.
+ */
+ Common::Rect textRect;
+
+ /**
+ * The color of the border. -1 indicates no border.
+ */
+ int16 borderColor;
+
+ /**
+ * The text color.
+ */
+ uint8 foreColor;
+
+ /**
+ * The background color.
+ */
+ uint8 backColor;
+
+ /**
+ * The transparent color.
+ */
+ uint8 skipColor;
+
+ /**
+ * The font used to render the text in the editor.
+ */
+ GuiResourceId fontId;
+
+ /**
+ * The current position of the cursor within the editor.
+ */
+ uint16 cursorCharPosition;
+
+ /**
+ * Whether or not the cursor is currently drawn to the
+ * screen.
+ */
+ bool cursorIsDrawn;
+
+ /**
+ * The rectangle for drawing the input cursor, in bitmap
+ * pixels.
+ */
+ Common::Rect cursorRect;
+
+ /**
+ * The maximum allowed text length, in characters.
+ */
+ uint16 maxLength;
+};
+
/**
* Controls class, handles drawing of controls in SCI32 (SCI2, SCI2.1, SCI3) games
*/
class GfxControls32 {
public:
GfxControls32(SegManager *segMan, GfxCache *cache, GfxText32 *text);
- ~GfxControls32();
- void kernelTexteditChange(reg_t controlObject);
+ reg_t kernelEditText(const reg_t controlObject);
private:
SegManager *_segMan;
- GfxCache *_cache;
- GfxText32 *_text;
+ GfxCache *_gfxCache;
+ GfxText32 *_gfxText32;
+
+ bool _overwriteMode;
+ uint32 _nextCursorFlashTick;
+ void drawCursor(TextEditor &editor);
+ void eraseCursor(TextEditor &editor);
+ void flashCursor(TextEditor &editor);
};
} // End of namespace Sci
diff --git a/engines/sci/graphics/cursor.cpp b/engines/sci/graphics/cursor.cpp
index 1a58de073c..f5dd473959 100644
--- a/engines/sci/graphics/cursor.cpp
+++ b/engines/sci/graphics/cursor.cpp
@@ -336,6 +336,9 @@ void GfxCursor::setPosition(Common::Point pos) {
&& ((workaround->newPositionX == pos.x) && (workaround->newPositionY == pos.y))) {
EngineState *s = g_sci->getEngineState();
s->_cursorWorkaroundActive = true;
+ // At least on OpenPandora it seems that the cursor is actually set, but a bit afterwards
+ // touch screen controls will overwrite the position. More information see kGetEvent in kevent.cpp.
+ s->_cursorWorkaroundPosCount = 5; // should be enough for OpenPandora
s->_cursorWorkaroundPoint = pos;
s->_cursorWorkaroundRect = Common::Rect(workaround->rectLeft, workaround->rectTop, workaround->rectRight, workaround->rectBottom);
return;
@@ -453,6 +456,15 @@ void GfxCursor::kernelClearZoomZone() {
void GfxCursor::kernelSetZoomZone(byte multiplier, Common::Rect zone, GuiResourceId viewNum, int loopNum, int celNum, GuiResourceId picNum, byte zoomColor) {
kernelClearZoomZone();
+ // This function is a stub in the Mac version of Freddy Pharkas.
+ // This function was only used in two games (LB2 and Pharkas), but there
+ // was no version of LB2 for the Macintosh platform.
+ // CHECKME: This wasn't verified against disassembly, one might want
+ // to check against it, in case there's some leftover code in the stubbed
+ // function (although it does seem that this was completely removed).
+ if (g_sci->getPlatform() == Common::kPlatformMacintosh)
+ return;
+
_zoomMultiplier = multiplier;
if (_zoomMultiplier != 1 && _zoomMultiplier != 2 && _zoomMultiplier != 4)
diff --git a/engines/sci/graphics/cursor.h b/engines/sci/graphics/cursor.h
index c2d7998eb3..8d125c45b3 100644
--- a/engines/sci/graphics/cursor.h
+++ b/engines/sci/graphics/cursor.h
@@ -77,8 +77,18 @@ public:
*/
void kernelSetMoveZone(Common::Rect zone);
- void kernelClearZoomZone();
+ /**
+ * Creates a dynamic zoom cursor, that is used to zoom on specific parts of the screen,
+ * using a separate larger picture. This was only used by two SCI1.1 games, Laura Bow 2
+ * (for examining the glyphs), and Freddy Pharkas (for examining the prescription with
+ * the whisky glass).
+ *
+ * In the Mac version of Freddy Pharkas, this was removed completely, and the scene has
+ * been redesigned to work without this functionality. There was no version of LB2 for
+ * the Macintosh platform.
+ */
void kernelSetZoomZone(byte multiplier, Common::Rect zone, GuiResourceId viewNum, int loopNum, int celNum, GuiResourceId picNum, byte zoomColor);
+ void kernelClearZoomZone();
void kernelSetPos(Common::Point pos);
void kernelMoveCursor(Common::Point pos);
diff --git a/engines/sci/graphics/frameout.cpp b/engines/sci/graphics/frameout.cpp
index cb81fe8d61..6454a1eb32 100644
--- a/engines/sci/graphics/frameout.cpp
+++ b/engines/sci/graphics/frameout.cpp
@@ -21,6 +21,7 @@
*/
#include "common/algorithm.h"
+#include "common/config-manager.h"
#include "common/events.h"
#include "common/keyboard.h"
#include "common/list.h"
@@ -46,964 +47,1484 @@
#include "sci/graphics/paint32.h"
#include "sci/graphics/palette32.h"
#include "sci/graphics/picture.h"
+#include "sci/graphics/remap.h"
#include "sci/graphics/text32.h"
+#include "sci/graphics/plane32.h"
+#include "sci/graphics/screen_item32.h"
#include "sci/graphics/frameout.h"
#include "sci/video/robot_decoder.h"
namespace Sci {
-// TODO/FIXME: This is all guesswork
-
-enum SciSpeciaPlanelPictureCodes {
- kPlaneTranslucent = 0xfffe, // -2
- kPlanePlainColored = 0xffff // -1
+static int dissolveSequences[2][20] = {
+ /* SCI2.1early- */ { 3, 6, 12, 20, 48, 96, 184, 272, 576, 1280, 3232, 6912, 13568, 24576, 46080 },
+ /* SCI2.1mid+ */ { 0, 0, 3, 6, 12, 20, 48, 96, 184, 272, 576, 1280, 3232, 6912, 13568, 24576, 46080, 73728, 132096, 466944 }
+};
+static int16 divisionsDefaults[2][16] = {
+ /* SCI2.1early- */ { 1, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 40, 40, 101, 101 },
+ /* SCI2.1mid+ */ { 1, 20, 20, 20, 20, 10, 10, 10, 10, 20, 20, 6, 10, 101, 101, 2 }
+};
+static int16 unknownCDefaults[2][16] = {
+ /* SCI2.1early- */ { 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 0, 0, 0, 0 },
+ /* SCI2.1mid+ */ { 0, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 0, 0, 7, 7, 0 }
};
-GfxFrameout::GfxFrameout(SegManager *segMan, ResourceManager *resMan, GfxCoordAdjuster *coordAdjuster, GfxCache *cache, GfxScreen *screen, GfxPalette32 *palette, GfxPaint32 *paint32)
- : _segMan(segMan), _resMan(resMan), _cache(cache), _screen(screen), _palette(palette), _paint32(paint32), _isHiRes(false) {
-
- _coordAdjuster = (GfxCoordAdjuster32 *)coordAdjuster;
- _curScrollText = -1;
- _showScrollText = false;
- _maxScrollTexts = 0;
+GfxFrameout::GfxFrameout(SegManager *segMan, ResourceManager *resMan, GfxCoordAdjuster *coordAdjuster, GfxCache *cache, GfxScreen *screen, GfxPalette32 *palette, GfxPaint32 *paint32) :
+ _isHiRes(false),
+ _cache(cache),
+ _palette(palette),
+ _resMan(resMan),
+ _screen(screen),
+ _segMan(segMan),
+ _paint32(paint32),
+ _showStyles(nullptr),
+ // TODO: Stop using _gfxScreen
+ _currentBuffer(screen->getDisplayWidth(), screen->getDisplayHeight(), nullptr),
+ _remapOccurred(false),
+ _frameNowVisible(false),
+ _screenRect(screen->getDisplayWidth(), screen->getDisplayHeight()),
+ _overdrawThreshold(0),
+ _palMorphIsOn(false) {
+
+ _currentBuffer.setPixels(calloc(1, screen->getDisplayWidth() * screen->getDisplayHeight()));
+
+ for (int i = 0; i < 236; i += 2) {
+ _styleRanges[i] = 0;
+ _styleRanges[i + 1] = -1;
+ }
+ for (int i = 236; i < ARRAYSIZE(_styleRanges); ++i) {
+ _styleRanges[i] = 0;
+ }
// TODO: Make hires detection work uniformly across all SCI engine
// versions (this flag is normally passed by SCI::MakeGraphicsMgr
- // to the GraphicsMgr constructor depending upon video configuration)
+ // to the GraphicsMgr constructor depending upon video configuration,
+ // so should be handled upstream based on game configuration instead
+ // of here)
if (getSciVersion() >= SCI_VERSION_2_1_EARLY && _resMan->detectHires()) {
_isHiRes = true;
}
+
+ if (getSciVersion() < SCI_VERSION_2_1_MIDDLE) {
+ _dissolveSequenceSeeds = dissolveSequences[0];
+ _defaultDivisions = divisionsDefaults[0];
+ _defaultUnknownC = unknownCDefaults[0];
+ } else {
+ _dissolveSequenceSeeds = dissolveSequences[1];
+ _defaultDivisions = divisionsDefaults[1];
+ _defaultUnknownC = unknownCDefaults[1];
+ }
+
+ switch (g_sci->getGameId()) {
+ case GID_GK2:
+ case GID_LIGHTHOUSE:
+ case GID_LSL7:
+ case GID_PHANTASMAGORIA2:
+ case GID_PQSWAT:
+ case GID_TORIN:
+ case GID_RAMA:
+ _currentBuffer.scriptWidth = 640;
+ _currentBuffer.scriptHeight = 480;
+ break;
+ default:
+ // default script width for other games is 320x200
+ break;
+ }
+
+ // TODO: Nothing in the renderer really uses this. Currently,
+ // the cursor renderer does, and kLocalToGlobal/kGlobalToLocal
+ // do, but in the real engine (1) the cursor is handled in
+ // frameOut, and (2) functions do a very simple lookup of the
+ // plane and arithmetic with the plane's gameRect. In
+ // principle, CoordAdjuster could be reused for
+ // convertGameRectToPlaneRect, but it is not super clear yet
+ // what the benefit would be to do that.
+ _coordAdjuster = (GfxCoordAdjuster32 *)coordAdjuster;
+
+ // TODO: Script resolution is hard-coded per game;
+ // also this must be set or else the engine will crash
+ _coordAdjuster->setScriptsResolution(_currentBuffer.scriptWidth, _currentBuffer.scriptHeight);
}
GfxFrameout::~GfxFrameout() {
clear();
+ CelObj::deinit();
+ free(_currentBuffer.getPixels());
}
+void GfxFrameout::run() {
+ CelObj::init();
+ Plane::init();
+ ScreenItem::init();
+
+ // NOTE: This happens in SCI::InitPlane in the actual engine,
+ // and is a background fill plane to ensure hidden planes
+ // (planes with a priority of -1) are never drawn
+ Plane *initPlane = new Plane(Common::Rect(_currentBuffer.scriptWidth, _currentBuffer.scriptHeight));
+ initPlane->_priority = 0;
+ _planes.add(initPlane);
+}
+
+// SCI32 actually did not clear anything at all it seems on restore. The scripts actually cleared up
+// planes + screen items right before restoring. And after restoring they sync'd its internal planes list
+// as well.
void GfxFrameout::clear() {
- deletePlaneItems(NULL_REG);
_planes.clear();
- deletePlanePictures(NULL_REG);
- clearScrollTexts();
-}
-
-void GfxFrameout::clearScrollTexts() {
- _scrollTexts.clear();
- _curScrollText = -1;
-}
-
-void GfxFrameout::addScrollTextEntry(Common::String &text, reg_t kWindow, uint16 x, uint16 y, bool replace) {
- //reg_t bitmapHandle = g_sci->_gfxText32->createScrollTextBitmap(text, kWindow);
- // HACK: We set the container dimensions manually
- reg_t bitmapHandle = g_sci->_gfxText32->createScrollTextBitmap(text, kWindow, 480, 70);
- ScrollTextEntry textEntry;
- textEntry.bitmapHandle = bitmapHandle;
- textEntry.kWindow = kWindow;
- textEntry.x = x;
- textEntry.y = y;
- if (!replace || _scrollTexts.size() == 0) {
- if (_scrollTexts.size() > _maxScrollTexts) {
- _scrollTexts.remove_at(0);
- _curScrollText--;
- }
- _scrollTexts.push_back(textEntry);
- _curScrollText++;
- } else {
- _scrollTexts.pop_back();
- _scrollTexts.push_back(textEntry);
- }
+ _visiblePlanes.clear();
+ _showList.clear();
}
-void GfxFrameout::showCurrentScrollText() {
- if (!_showScrollText || _curScrollText < 0)
+// This is what Game::restore does, only needed when our ScummVM dialogs are patched in
+// It actually does one pass before actual restore deleting screen items + planes
+// And after restore it does another pass adding screen items + planes.
+// Attention: at least Space Quest 6's option plane seems to stay in memory right from the start and is not re-created.
+void GfxFrameout::syncWithScripts(bool addElements) {
+ EngineState *engineState = g_sci->getEngineState();
+ SegManager *segMan = engineState->_segMan;
+
+ // In case original save/restore dialogs are active, don't do anything
+ if (ConfMan.getBool("originalsaveload"))
return;
- uint16 size = (uint16)_scrollTexts.size();
- if (size > 0) {
- assert(_curScrollText < size);
- ScrollTextEntry textEntry = _scrollTexts[_curScrollText];
- g_sci->_gfxText32->drawScrollTextBitmap(textEntry.kWindow, textEntry.bitmapHandle, textEntry.x, textEntry.y);
- }
-}
-
-extern void showScummVMDialog(const Common::String &message);
-
-void GfxFrameout::kernelAddPlane(reg_t object) {
- PlaneEntry newPlane;
-
- if (_planes.empty()) {
- // There has to be another way for sierra sci to do this or maybe script resolution is compiled into
- // interpreter (TODO)
- uint16 scriptWidth = readSelectorValue(_segMan, object, SELECTOR(resX));
- uint16 scriptHeight = readSelectorValue(_segMan, object, SELECTOR(resY));
-
- // Phantasmagoria 2 doesn't specify a script width/height
- if (g_sci->getGameId() == GID_PHANTASMAGORIA2) {
- scriptWidth = 640;
- scriptHeight = 480;
- }
-
- assert(scriptWidth > 0 && scriptHeight > 0);
- _coordAdjuster->setScriptsResolution(scriptWidth, scriptHeight);
- }
-
- // Import of QfG character files dialog is shown in QFG4.
- // Display additional popup information before letting user use it.
- // For the SCI0-SCI1.1 version of this, check kDrawControl().
- if (g_sci->inQfGImportRoom() && !strcmp(_segMan->getObjectName(object), "DSPlane")) {
- showScummVMDialog("Characters saved inside ScummVM are shown "
- "automatically. Character files saved in the original "
- "interpreter need to be put inside ScummVM's saved games "
- "directory and a prefix needs to be added depending on which "
- "game it was saved in: 'qfg1-' for Quest for Glory 1, 'qfg2-' "
- "for Quest for Glory 2, 'qfg3-' for Quest for Glory 3. "
- "Example: 'qfg2-thief.sav'.");
- }
-
- newPlane.object = object;
- newPlane.priority = readSelectorValue(_segMan, object, SELECTOR(priority));
- newPlane.lastPriority = -1; // hidden
- newPlane.planeOffsetX = 0;
- newPlane.planeOffsetY = 0;
- newPlane.pictureId = kPlanePlainColored;
- newPlane.planePictureMirrored = false;
- newPlane.planeBack = 0;
- _planes.push_back(newPlane);
-
- kernelUpdatePlane(object);
-}
-
-void GfxFrameout::kernelUpdatePlane(reg_t object) {
- for (PlaneList::iterator it = _planes.begin(); it != _planes.end(); ++it) {
- if (it->object == object) {
- // Read some information
- it->priority = readSelectorValue(_segMan, object, SELECTOR(priority));
- GuiResourceId lastPictureId = it->pictureId;
- it->pictureId = readSelectorValue(_segMan, object, SELECTOR(picture));
- if (lastPictureId != it->pictureId) {
- // picture got changed, load new picture
- deletePlanePictures(object);
- // Draw the plane's picture if it's not a translucent/plane colored frame
- if ((it->pictureId != kPlanePlainColored) && (it->pictureId != kPlaneTranslucent)) {
- // SQ6 gives us a bad picture number for the control menu
- if (_resMan->testResource(ResourceId(kResourceTypePic, it->pictureId)))
- addPlanePicture(object, it->pictureId, 0);
- }
- }
- it->planeRect.top = readSelectorValue(_segMan, object, SELECTOR(top));
- it->planeRect.left = readSelectorValue(_segMan, object, SELECTOR(left));
- it->planeRect.bottom = readSelectorValue(_segMan, object, SELECTOR(bottom));
- it->planeRect.right = readSelectorValue(_segMan, object, SELECTOR(right));
-
- _coordAdjuster->fromScriptToDisplay(it->planeRect.top, it->planeRect.left);
- _coordAdjuster->fromScriptToDisplay(it->planeRect.bottom, it->planeRect.right);
-
- // We get negative left in kq7 in scrolling rooms
- if (it->planeRect.left < 0) {
- it->planeOffsetX = -it->planeRect.left;
- it->planeRect.left = 0;
- } else {
- it->planeOffsetX = 0;
- }
+ // Get planes list object
+ reg_t planesListObject = engineState->variables[VAR_GLOBAL][10];
+ reg_t planesListElements = readSelector(segMan, planesListObject, SELECTOR(elements));
- if (it->planeRect.top < 0) {
- it->planeOffsetY = -it->planeRect.top;
- it->planeRect.top = 0;
- } else {
- it->planeOffsetY = 0;
- }
+ List *planesList = segMan->lookupList(planesListElements);
+ reg_t planesNodeObject = planesList->first;
- // We get bad plane-bottom in sq6
- if (it->planeRect.right > _screen->getWidth())
- it->planeRect.right = _screen->getWidth();
- if (it->planeRect.bottom > _screen->getHeight())
- it->planeRect.bottom = _screen->getHeight();
-
- it->planeClipRect = Common::Rect(it->planeRect.width(), it->planeRect.height());
- it->upscaledPlaneRect = it->planeRect;
- it->upscaledPlaneClipRect = it->planeClipRect;
- if (_screen->getUpscaledHires()) {
- _screen->adjustToUpscaledCoordinates(it->upscaledPlaneRect.top, it->upscaledPlaneRect.left);
- _screen->adjustToUpscaledCoordinates(it->upscaledPlaneRect.bottom, it->upscaledPlaneRect.right);
- _screen->adjustToUpscaledCoordinates(it->upscaledPlaneClipRect.top, it->upscaledPlaneClipRect.left);
- _screen->adjustToUpscaledCoordinates(it->upscaledPlaneClipRect.bottom, it->upscaledPlaneClipRect.right);
- }
+ // Go through all elements of planes::elements
+ while (!planesNodeObject.isNull()) {
+ Node *planesNode = segMan->lookupNode(planesNodeObject);
+ reg_t planeObject = planesNode->value;
+
+ if (addElements) {
+ // Add this plane object
+ kernelAddPlane(planeObject);
+ }
+
+ reg_t planeCastsObject = readSelector(segMan, planeObject, SELECTOR(casts));
+ reg_t setListElements = readSelector(segMan, planeCastsObject, SELECTOR(elements));
+
+ // Now go through all elements of plane::casts::elements
+ List *planeCastsList = segMan->lookupList(setListElements);
+ reg_t planeCastsNodeObject = planeCastsList->first;
+
+ while (!planeCastsNodeObject.isNull()) {
+ Node *castsNode = segMan->lookupNode(planeCastsNodeObject);
+ reg_t castsObject = castsNode->value;
+
+ reg_t castsListElements = readSelector(segMan, castsObject, SELECTOR(elements));
+
+ List *castsList = segMan->lookupList(castsListElements);
+ reg_t castNodeObject = castsList->first;
- it->planePictureMirrored = readSelectorValue(_segMan, object, SELECTOR(mirrored));
- it->planeBack = readSelectorValue(_segMan, object, SELECTOR(back));
+ while (!castNodeObject.isNull()) {
+ Node *castNode = segMan->lookupNode(castNodeObject);
+ reg_t castObject = castNode->value;
- sortPlanes();
+ // read selector "-info-" of this object
+ // TODO: Seems to have been changed for SCI3
+ // Do NOT use getInfoSelector in here. SCI3 games did not use infoToa, but an actual selector.
+ // Maybe that selector is just a straight copy, but it needs to get verified/checked.
+ uint16 castInfoSelector = readSelectorValue(segMan, castObject, SELECTOR(_info_));
- // Update the items in the plane
- for (FrameoutList::iterator listIterator = _screenItems.begin(); listIterator != _screenItems.end(); listIterator++) {
- reg_t itemPlane = readSelector(_segMan, (*listIterator)->object, SELECTOR(plane));
- if (object == itemPlane) {
- kernelUpdateScreenItem((*listIterator)->object);
+ if (castInfoSelector & kInfoFlagViewInserted) {
+ if (addElements) {
+ // Flag set, so add this screen item
+ kernelAddScreenItem(castObject);
+ } else {
+ // Flag set, so delete this screen item
+ kernelDeleteScreenItem(castObject);
+ }
}
+
+ castNodeObject = castNode->succ;
}
- return;
+ planeCastsNodeObject = castsNode->succ;
+ }
+
+ if (!addElements) {
+ // Delete this plane object
+ kernelDeletePlane(planeObject);
}
+
+ planesNodeObject = planesNode->succ;
}
- error("kUpdatePlane called on plane that wasn't added before");
}
-void GfxFrameout::kernelDeletePlane(reg_t object) {
- deletePlaneItems(object);
- deletePlanePictures(object);
+#pragma mark -
+#pragma mark Screen items
+
+void GfxFrameout::kernelAddScreenItem(const reg_t object) {
+ const reg_t planeObject = readSelector(_segMan, object, SELECTOR(plane));
- for (PlaneList::iterator it = _planes.begin(); it != _planes.end(); ++it) {
- if (it->object == object) {
- _planes.erase(it);
- Common::Rect planeRect;
- planeRect.top = readSelectorValue(_segMan, object, SELECTOR(top));
- planeRect.left = readSelectorValue(_segMan, object, SELECTOR(left));
- planeRect.bottom = readSelectorValue(_segMan, object, SELECTOR(bottom));
- planeRect.right = readSelectorValue(_segMan, object, SELECTOR(right));
+ _segMan->getObject(object)->setInfoSelectorFlag(kInfoFlagViewInserted);
+
+ Plane *plane = _planes.findByObject(planeObject);
+ if (plane == nullptr) {
+ error("kAddScreenItem: Plane %04x:%04x not found for screen item %04x:%04x", PRINT_REG(planeObject), PRINT_REG(object));
+ }
+
+ ScreenItem *screenItem = plane->_screenItemList.findByObject(object);
+ if (screenItem != nullptr) {
+ screenItem->update(object);
+ } else {
+ screenItem = new ScreenItem(object);
+ plane->_screenItemList.add(screenItem);
+ }
+}
- _coordAdjuster->fromScriptToDisplay(planeRect.top, planeRect.left);
- _coordAdjuster->fromScriptToDisplay(planeRect.bottom, planeRect.right);
+void GfxFrameout::kernelUpdateScreenItem(const reg_t object) {
+ const reg_t magnifierObject = readSelector(_segMan, object, SELECTOR(magnifier));
+ if (magnifierObject.isNull()) {
+ const reg_t planeObject = readSelector(_segMan, object, SELECTOR(plane));
+ Plane *plane = _planes.findByObject(planeObject);
+ if (plane == nullptr) {
+ error("kUpdateScreenItem: Plane %04x:%04x not found for screen item %04x:%04x", PRINT_REG(planeObject), PRINT_REG(object));
+ }
- // Blackout removed plane rect
- _paint32->fillRect(planeRect, 0);
- return;
+ ScreenItem *screenItem = plane->_screenItemList.findByObject(object);
+ if (screenItem == nullptr) {
+ error("kUpdateScreenItem: Screen item %04x:%04x not found in plane %04x:%04x", PRINT_REG(object), PRINT_REG(planeObject));
}
+
+ screenItem->update(object);
+ } else {
+ error("Magnifier view is not known to be used by any game. Please submit a bug report with details about the game you were playing and what you were doing that triggered this error. Thanks!");
}
}
-void GfxFrameout::addPlanePicture(reg_t object, GuiResourceId pictureId, uint16 startX, uint16 startY) {
- if (pictureId == kPlanePlainColored || pictureId == kPlaneTranslucent) // sanity check
+void GfxFrameout::kernelDeleteScreenItem(const reg_t object) {
+ _segMan->getObject(object)->clearInfoSelectorFlag(kInfoFlagViewInserted);
+
+ const reg_t planeObject = readSelector(_segMan, object, SELECTOR(plane));
+ Plane *plane = _planes.findByObject(planeObject);
+ if (plane == nullptr) {
+ return;
+ }
+
+ ScreenItem *screenItem = plane->_screenItemList.findByObject(object);
+ if (screenItem == nullptr) {
return;
+ }
- PlanePictureEntry newPicture;
- newPicture.object = object;
- newPicture.pictureId = pictureId;
- newPicture.picture = new GfxPicture(_resMan, _coordAdjuster, 0, _screen, _palette, pictureId, false);
- newPicture.startX = startX;
- newPicture.startY = startY;
- newPicture.pictureCels = 0;
- _planePictures.push_back(newPicture);
+ if (screenItem->_created == 0) {
+ screenItem->_created = 0;
+ screenItem->_updated = 0;
+ screenItem->_deleted = getScreenCount();
+ } else {
+ plane->_screenItemList.erase(screenItem);
+ plane->_screenItemList.pack();
+ }
}
-void GfxFrameout::deletePlanePictures(reg_t object) {
- PlanePictureList::iterator it = _planePictures.begin();
+#pragma mark -
+#pragma mark Planes
- while (it != _planePictures.end()) {
- if (it->object == object || object.isNull()) {
- delete it->pictureCels;
- delete it->picture;
- it = _planePictures.erase(it);
- } else {
- ++it;
- }
+void GfxFrameout::kernelAddPlane(const reg_t object) {
+ Plane *plane = _planes.findByObject(object);
+ if (plane != nullptr) {
+ plane->update(object);
+ updatePlane(*plane);
+ } else {
+ plane = new Plane(object);
+ addPlane(*plane);
}
}
-// Provides the same functionality as kGraph(DrawLine)
-reg_t GfxFrameout::addPlaneLine(reg_t object, Common::Point startPoint, Common::Point endPoint, byte color, byte priority, byte control) {
- for (PlaneList::iterator it = _planes.begin(); it != _planes.end(); ++it) {
- if (it->object == object) {
- PlaneLineEntry line;
- line.hunkId = _segMan->allocateHunkEntry("PlaneLine()", 1); // we basically use this for a unique ID
- line.startPoint = startPoint;
- line.endPoint = endPoint;
- line.color = color;
- line.priority = priority;
- line.control = control;
- it->lines.push_back(line);
- return line.hunkId;
- }
+void GfxFrameout::kernelUpdatePlane(const reg_t object) {
+ Plane *plane = _planes.findByObject(object);
+ if (plane == nullptr) {
+ error("kUpdatePlane: Plane %04x:%04x not found", PRINT_REG(object));
}
- return NULL_REG;
+ plane->update(object);
+ updatePlane(*plane);
}
-void GfxFrameout::updatePlaneLine(reg_t object, reg_t hunkId, Common::Point startPoint, Common::Point endPoint, byte color, byte priority, byte control) {
- // Check if we're asked to update a line that was never added
- if (hunkId.isNull())
- return;
+void GfxFrameout::kernelDeletePlane(const reg_t object) {
+ Plane *plane = _planes.findByObject(object);
+ if (plane == nullptr) {
+ error("kDeletePlane: Plane %04x:%04x not found", PRINT_REG(object));
+ }
- for (PlaneList::iterator it = _planes.begin(); it != _planes.end(); ++it) {
- if (it->object == object) {
- for (PlaneLineList::iterator it2 = it->lines.begin(); it2 != it->lines.end(); ++it2) {
- if (it2->hunkId == hunkId) {
- it2->startPoint = startPoint;
- it2->endPoint = endPoint;
- it2->color = color;
- it2->priority = priority;
- it2->control = control;
- return;
- }
- }
- }
+ if (plane->_created) {
+ // NOTE: The original engine calls some `AbortPlane` function that
+ // just ends up doing this anyway so we skip the extra indirection
+ _planes.erase(plane);
+ } else {
+ plane->_created = 0;
+ plane->_deleted = g_sci->_gfxFrameout->getScreenCount();
}
}
-void GfxFrameout::deletePlaneLine(reg_t object, reg_t hunkId) {
- // Check if we're asked to delete a line that was never added (happens during the intro of LSL6)
- if (hunkId.isNull())
- return;
+void GfxFrameout::deletePlane(Plane &planeToFind) {
+ Plane *plane = _planes.findByObject(planeToFind._object);
+ if (plane == nullptr) {
+ error("deletePlane: Plane %04x:%04x not found", PRINT_REG(planeToFind._object));
+ }
- for (PlaneList::iterator it = _planes.begin(); it != _planes.end(); ++it) {
- if (it->object == object) {
- for (PlaneLineList::iterator it2 = it->lines.begin(); it2 != it->lines.end(); ++it2) {
- if (it2->hunkId == hunkId) {
- _segMan->freeHunkEntry(hunkId);
- it2 = it->lines.erase(it2);
- return;
- }
- }
- }
+ if (plane->_created) {
+ _planes.erase(plane);
+ } else {
+ plane->_created = 0;
+ plane->_moved = 0;
+ plane->_deleted = getScreenCount();
}
}
-// Adapted from GfxAnimate::applyGlobalScaling()
-void GfxFrameout::applyGlobalScaling(FrameoutEntry *itemEntry, Common::Rect planeRect, int16 celHeight) {
- // Global scaling uses global var 2 and some other stuff to calculate scaleX/scaleY
- int16 maxScale = readSelectorValue(_segMan, itemEntry->object, SELECTOR(maxScale));
- int16 maxCelHeight = (maxScale * celHeight) >> 7;
- reg_t globalVar2 = g_sci->getEngineState()->variables[VAR_GLOBAL][2]; // current room object
- int16 vanishingY = readSelectorValue(_segMan, globalVar2, SELECTOR(vanishingY));
+void GfxFrameout::kernelMovePlaneItems(const reg_t object, const int16 deltaX, const int16 deltaY, const bool scrollPics) {
+ Plane *plane = _planes.findByObject(object);
+ if (plane == nullptr) {
+ error("kMovePlaneItems: Plane %04x:%04x not found", PRINT_REG(object));
+ }
- int16 fixedPortY = planeRect.bottom - vanishingY;
- int16 fixedEntryY = itemEntry->y - vanishingY;
- if (!fixedEntryY)
- fixedEntryY = 1;
+ plane->scrollScreenItems(deltaX, deltaY, scrollPics);
- if ((celHeight == 0) || (fixedPortY == 0))
- error("global scaling panic");
+ for (ScreenItemList::iterator it = plane->_screenItemList.begin(); it != plane->_screenItemList.end(); ++it) {
+ ScreenItem &screenItem = **it;
- itemEntry->scaleY = (maxCelHeight * fixedEntryY) / fixedPortY;
- itemEntry->scaleY = (itemEntry->scaleY * maxScale) / celHeight;
+ // If object is a number, the screen item from the
+ // engine, not a script, and should be ignored
+ if (screenItem._object.isNumber()) {
+ continue;
+ }
- // Make sure that the calculated value is sane
- if (itemEntry->scaleY < 1 /*|| itemEntry->scaleY > 128*/)
- itemEntry->scaleY = 128;
+ if (deltaX != 0) {
+ writeSelectorValue(_segMan, screenItem._object, SELECTOR(x), readSelectorValue(_segMan, screenItem._object, SELECTOR(x)) + deltaX);
+ }
- itemEntry->scaleX = itemEntry->scaleY;
+ if (deltaY != 0) {
+ writeSelectorValue(_segMan, screenItem._object, SELECTOR(y), readSelectorValue(_segMan, screenItem._object, SELECTOR(y)) + deltaY);
+ }
+ }
+}
- // and set objects scale selectors
- //writeSelectorValue(_segMan, itemEntry->object, SELECTOR(scaleX), itemEntry->scaleX);
- //writeSelectorValue(_segMan, itemEntry->object, SELECTOR(scaleY), itemEntry->scaleY);
+int16 GfxFrameout::kernelGetHighPlanePri() {
+ return _planes.getTopSciPlanePriority();
}
-void GfxFrameout::kernelAddScreenItem(reg_t object) {
- // Ignore invalid items
- if (!_segMan->isObject(object)) {
- warning("kernelAddScreenItem: Attempt to add an invalid object (%04x:%04x)", PRINT_REG(object));
- return;
+void GfxFrameout::addPlane(Plane &plane) {
+ if (_planes.findByObject(plane._object) == nullptr) {
+ plane.clipScreenRect(_screenRect);
+ _planes.add(&plane);
+ } else {
+ plane._deleted = 0;
+ if (plane._created == 0) {
+ plane._moved = g_sci->_gfxFrameout->getScreenCount();
+ }
+ _planes.sort();
}
+}
+
+void GfxFrameout::updatePlane(Plane &plane) {
+ // NOTE: This assertion comes from SCI engine code.
+ assert(_planes.findByObject(plane._object) == &plane);
- FrameoutEntry *itemEntry = new FrameoutEntry();
- memset(itemEntry, 0, sizeof(FrameoutEntry));
- itemEntry->object = object;
- itemEntry->givenOrderNr = _screenItems.size();
- itemEntry->visible = true;
- _screenItems.push_back(itemEntry);
+ Plane *visiblePlane = _visiblePlanes.findByObject(plane._object);
+ plane.sync(visiblePlane, _screenRect);
+ // NOTE: updateScreenRect was originally called a second time here,
+ // but it is already called at the end of the Plane::Update call
+ // in the original engine anyway.
- kernelUpdateScreenItem(object);
+ _planes.sort();
}
-void GfxFrameout::kernelUpdateScreenItem(reg_t object) {
- // Ignore invalid items
- if (!_segMan->isObject(object)) {
- warning("kernelUpdateScreenItem: Attempt to update an invalid object (%04x:%04x)", PRINT_REG(object));
- return;
- }
+#pragma mark -
+#pragma mark Pics
- FrameoutEntry *itemEntry = findScreenItem(object);
- if (!itemEntry) {
- warning("kernelUpdateScreenItem: invalid object %04x:%04x", PRINT_REG(object));
- return;
+void GfxFrameout::kernelAddPicAt(const reg_t planeObject, const GuiResourceId pictureId, const int16 x, const int16 y, const bool mirrorX) {
+ Plane *plane = _planes.findByObject(planeObject);
+ if (plane == nullptr) {
+ error("kAddPicAt: Plane %04x:%04x not found", PRINT_REG(planeObject));
}
+ plane->addPic(pictureId, Common::Point(x, y), mirrorX);
+}
- itemEntry->viewId = readSelectorValue(_segMan, object, SELECTOR(view));
- itemEntry->loopNo = readSelectorValue(_segMan, object, SELECTOR(loop));
- itemEntry->celNo = readSelectorValue(_segMan, object, SELECTOR(cel));
- itemEntry->x = readSelectorValue(_segMan, object, SELECTOR(x));
- itemEntry->y = readSelectorValue(_segMan, object, SELECTOR(y));
- itemEntry->z = readSelectorValue(_segMan, object, SELECTOR(z));
- itemEntry->priority = readSelectorValue(_segMan, object, SELECTOR(priority));
- if (readSelectorValue(_segMan, object, SELECTOR(fixPriority)) == 0)
- itemEntry->priority = itemEntry->y;
+#pragma mark -
+#pragma mark Rendering
- itemEntry->signal = readSelectorValue(_segMan, object, SELECTOR(signal));
- itemEntry->scaleSignal = readSelectorValue(_segMan, object, SELECTOR(scaleSignal));
+void GfxFrameout::frameOut(const bool shouldShowBits, const Common::Rect &rect) {
+// TODO: Robot
+// if (_robot != nullptr) {
+// _robot.doRobot();
+// }
- if (itemEntry->scaleSignal & kScaleSignalDoScaling32) {
- itemEntry->scaleX = readSelectorValue(_segMan, object, SELECTOR(scaleX));
- itemEntry->scaleY = readSelectorValue(_segMan, object, SELECTOR(scaleY));
- } else {
- itemEntry->scaleX = 128;
- itemEntry->scaleY = 128;
- }
- itemEntry->visible = true;
+ // NOTE: The original engine allocated these as static arrays of 100
+ // pointers to ScreenItemList / RectList
+ ScreenItemListList screenItemLists;
+ EraseListList eraseLists;
- // Check if the entry can be hidden
- if (lookupSelector(_segMan, object, SELECTOR(visible), NULL, NULL) != kSelectorNone)
- itemEntry->visible = readSelectorValue(_segMan, object, SELECTOR(visible));
-}
+ screenItemLists.resize(_planes.size());
+ eraseLists.resize(_planes.size());
-void GfxFrameout::kernelDeleteScreenItem(reg_t object) {
- FrameoutEntry *itemEntry = findScreenItem(object);
- // If the item could not be found, it may already have been deleted
- if (!itemEntry)
- return;
+ if (g_sci->_gfxRemap32->getRemapCount() > 0 && _remapOccurred) {
+ remapMarkRedraw();
+ }
- _screenItems.remove(itemEntry);
- delete itemEntry;
-}
+ calcLists(screenItemLists, eraseLists, rect);
-void GfxFrameout::deletePlaneItems(reg_t planeObject) {
- FrameoutList::iterator listIterator = _screenItems.begin();
+ for (ScreenItemListList::iterator list = screenItemLists.begin(); list != screenItemLists.end(); ++list) {
+ list->sort();
+ }
- while (listIterator != _screenItems.end()) {
- bool objectMatches = false;
- if (!planeObject.isNull()) {
- reg_t itemPlane = readSelector(_segMan, (*listIterator)->object, SELECTOR(plane));
- objectMatches = (planeObject == itemPlane);
- } else {
- objectMatches = true;
+ for (ScreenItemListList::iterator list = screenItemLists.begin(); list != screenItemLists.end(); ++list) {
+ for (DrawList::iterator drawItem = list->begin(); drawItem != list->end(); ++drawItem) {
+ (*drawItem)->screenItem->getCelObj().submitPalette();
}
+ }
- if (objectMatches) {
- FrameoutEntry *itemEntry = *listIterator;
- listIterator = _screenItems.erase(listIterator);
- delete itemEntry;
- } else {
- ++listIterator;
- }
+ _remapOccurred = _palette->updateForFrame();
+
+ // NOTE: SCI engine set this to false on each loop through the
+ // planelist iterator below. Since that is a waste, we only set
+ // it once.
+ _frameNowVisible = false;
+
+ for (PlaneList::size_type i = 0; i < _planes.size(); ++i) {
+ drawEraseList(eraseLists[i], *_planes[i]);
+ drawScreenItemList(screenItemLists[i]);
}
-}
-FrameoutEntry *GfxFrameout::findScreenItem(reg_t object) {
- for (FrameoutList::iterator listIterator = _screenItems.begin(); listIterator != _screenItems.end(); listIterator++) {
- FrameoutEntry *itemEntry = *listIterator;
- if (itemEntry->object == object)
- return itemEntry;
+// TODO: Robot
+// if (_robot != nullptr) {
+// _robot->frameAlmostVisible();
+// }
+
+ _palette->updateHardware();
+
+ if (shouldShowBits) {
+ showBits();
}
- return NULL;
-}
+ _frameNowVisible = true;
-int16 GfxFrameout::kernelGetHighPlanePri() {
- sortPlanes();
- return readSelectorValue(g_sci->getEngineState()->_segMan, _planes.back().object, SELECTOR(priority));
+// TODO: Robot
+// if (_robot != nullptr) {
+// robot->frameNowVisible();
+// }
}
-void GfxFrameout::kernelAddPicAt(reg_t planeObj, GuiResourceId pictureId, int16 pictureX, int16 pictureY) {
- addPlanePicture(planeObj, pictureId, pictureX, pictureY);
-}
+// Determine the parts of 'r' that aren't overlapped by 'other'.
+// Returns -1 if r and other have no intersection.
+// Returns number of returned parts (in outRects) otherwise.
+// (In particular, this returns 0 if r is contained in other.)
+int splitRects(Common::Rect r, const Common::Rect &other, Common::Rect(&outRects)[4]) {
+ if (!r.intersects(other)) {
+ return -1;
+ }
-bool sortHelper(const FrameoutEntry* entry1, const FrameoutEntry* entry2) {
- if (entry1->priority == entry2->priority) {
- if (entry1->y == entry2->y)
- return (entry1->givenOrderNr < entry2->givenOrderNr);
- return (entry1->y < entry2->y);
+ int count = 0;
+ if (r.top < other.top) {
+ Common::Rect &t = outRects[count++];
+ t = r;
+ t.bottom = other.top;
+ r.top = other.top;
}
- return (entry1->priority < entry2->priority);
-}
-bool planeSortHelper(const PlaneEntry &entry1, const PlaneEntry &entry2) {
- if (entry1.priority < 0)
- return true;
+ if (r.bottom > other.bottom) {
+ Common::Rect &t = outRects[count++];
+ t = r;
+ t.top = other.bottom;
+ r.bottom = other.bottom;
+ }
- if (entry2.priority < 0)
- return false;
+ if (r.left < other.left) {
+ Common::Rect &t = outRects[count++];
+ t = r;
+ t.right = other.left;
+ r.left = other.left;
+ }
- return entry1.priority < entry2.priority;
+ if (r.right > other.right) {
+ Common::Rect &t = outRects[count++];
+ t = r;
+ t.left = other.right;
+ }
+
+ return count;
}
-void GfxFrameout::sortPlanes() {
- // First, remove any invalid planes
- for (PlaneList::iterator it = _planes.begin(); it != _planes.end();) {
- if (!_segMan->isObject(it->object))
- it = _planes.erase(it);
- else
- it++;
+void GfxFrameout::calcLists(ScreenItemListList &drawLists, EraseListList &eraseLists, const Common::Rect &calcRect) {
+ RectList rectlist;
+ Common::Rect outRects[4];
+
+ int deletedPlaneCount = 0;
+ bool addedToRectList = false;
+ int planeCount = _planes.size();
+ bool foundTransparentPlane = false;
+
+ if (!calcRect.isEmpty()) {
+ addedToRectList = true;
+ rectlist.add(calcRect);
}
- // Sort the rest of them
- Common::sort(_planes.begin(), _planes.end(), planeSortHelper);
-}
+ for (int outerPlaneIndex = 0; outerPlaneIndex < planeCount; ++outerPlaneIndex) {
+ Plane *outerPlane = _planes[outerPlaneIndex];
-void GfxFrameout::showVideo() {
- bool skipVideo = false;
- RobotDecoder *videoDecoder = g_sci->_robotDecoder;
- uint16 x = videoDecoder->getPos().x;
- uint16 y = videoDecoder->getPos().y;
- uint16 screenWidth = _screen->getWidth();
- uint16 screenHeight = _screen->getHeight();
- uint16 outputWidth;
- uint16 outputHeight;
+ if (outerPlane->_type == kPlaneTypeTransparent) {
+ foundTransparentPlane = true;
+ }
- if (videoDecoder->hasDirtyPalette())
- g_system->getPaletteManager()->setPalette(videoDecoder->getPalette(), 0, 256);
+ Plane *visiblePlane = _visiblePlanes.findByObject(outerPlane->_object);
- while (!g_engine->shouldQuit() && !videoDecoder->endOfVideo() && !skipVideo) {
- if (videoDecoder->needsUpdate()) {
- const Graphics::Surface *frame = videoDecoder->decodeNextFrame();
- if (frame) {
- // We need to clip here
- // At least Phantasmagoria shows a 640x390 video on a 630x450 screen during the intro
- outputWidth = frame->w > screenWidth ? screenWidth : frame->w;
- outputHeight = frame->h > screenHeight ? screenHeight : frame->h;
- g_system->copyRectToScreen(frame->getPixels(), frame->pitch, x, y, outputWidth, outputHeight);
+ if (outerPlane->_deleted) {
+ if (visiblePlane != nullptr) {
+ if (!visiblePlane->_screenRect.isEmpty()) {
+ addedToRectList = true;
+ rectlist.add(visiblePlane->_screenRect);
+ }
+ }
+ ++deletedPlaneCount;
+ } else if (visiblePlane != nullptr) {
+ if (outerPlane->_updated) {
+ --outerPlane->_updated;
+
+ int splitcount = splitRects(visiblePlane->_screenRect, outerPlane->_screenRect, outRects);
+ if (splitcount) {
+ if (splitcount == -1) {
+ if (!visiblePlane->_screenRect.isEmpty()) {
+ rectlist.add(visiblePlane->_screenRect);
+ }
+ } else {
+ for (int i = 0; i < splitcount; ++i) {
+ rectlist.add(outRects[i]);
+ }
+ }
- if (videoDecoder->hasDirtyPalette())
- g_system->getPaletteManager()->setPalette(videoDecoder->getPalette(), 0, 256);
+ addedToRectList = true;
+ }
- g_system->updateScreen();
+ if (!outerPlane->_redrawAllCount) {
+ int splitCount = splitRects(outerPlane->_screenRect, visiblePlane->_screenRect, outRects);
+ if (splitCount) {
+ for (int i = 0; i < splitCount; ++i) {
+ rectlist.add(outRects[i]);
+ }
+ addedToRectList = true;
+ }
+ }
}
}
- Common::Event event;
- while (g_system->getEventManager()->pollEvent(event)) {
- if ((event.type == Common::EVENT_KEYDOWN && event.kbd.keycode == Common::KEYCODE_ESCAPE) || event.type == Common::EVENT_LBUTTONUP)
- skipVideo = true;
- }
+ if (addedToRectList) {
+ for (RectList::iterator rect = rectlist.begin(); rect != rectlist.end(); ++rect) {
+ for (int innerPlaneIndex = _planes.size() - 1; innerPlaneIndex >= 0; --innerPlaneIndex) {
+ Plane *innerPlane = _planes[innerPlaneIndex];
+
+ if (!innerPlane->_deleted && innerPlane->_type != kPlaneTypeTransparent && innerPlane->_screenRect.intersects(**rect)) {
+ if (innerPlane->_redrawAllCount == 0) {
+ eraseLists[innerPlaneIndex].add(innerPlane->_screenRect.findIntersectingRect(**rect));
+ }
+
+ int splitCount = splitRects(**rect, innerPlane->_screenRect, outRects);
+ for (int i = 0; i < splitCount; ++i) {
+ rectlist.add(outRects[i]);
+ }
+
+ rectlist.erase(rect);
+ break;
+ }
+ }
+ }
- g_system->delayMillis(10);
+ rectlist.pack();
+ }
}
-}
-void GfxFrameout::createPlaneItemList(reg_t planeObject, FrameoutList &itemList) {
- // Copy screen items of the current frame to the list of items to be drawn
- for (FrameoutList::iterator listIterator = _screenItems.begin(); listIterator != _screenItems.end(); listIterator++) {
- reg_t itemPlane = readSelector(_segMan, (*listIterator)->object, SELECTOR(plane));
- if (planeObject == itemPlane) {
- kernelUpdateScreenItem((*listIterator)->object); // TODO: Why is this necessary?
- itemList.push_back(*listIterator);
+ // clean up deleted planes
+ if (deletedPlaneCount) {
+ for (int planeIndex = planeCount - 1; planeIndex >= 0; --planeIndex) {
+ Plane *plane = _planes[planeIndex];
+
+ if (plane->_deleted) {
+ --plane->_deleted;
+ if (plane->_deleted <= 0) {
+ PlaneList::iterator visiblePlaneIt = Common::find_if(_visiblePlanes.begin(), _visiblePlanes.end(), FindByObject<Plane *>(plane->_object));
+ if (visiblePlaneIt != _visiblePlanes.end()) {
+ _visiblePlanes.erase(visiblePlaneIt);
+ }
+
+ _planes.remove_at(planeIndex);
+ eraseLists.remove_at(planeIndex);
+ drawLists.remove_at(planeIndex);
+ }
+
+ if (--deletedPlaneCount <= 0) {
+ break;
+ }
+ }
}
}
- for (PlanePictureList::iterator pictureIt = _planePictures.begin(); pictureIt != _planePictures.end(); pictureIt++) {
- if (pictureIt->object == planeObject) {
- GfxPicture *planePicture = pictureIt->picture;
- // Allocate memory for picture cels
- pictureIt->pictureCels = new FrameoutEntry[planePicture->getSci32celCount()];
+ planeCount = _planes.size();
+ for (int outerIndex = 0; outerIndex < planeCount; ++outerIndex) {
+ // "outer" just refers to the outer loop
+ Plane *outerPlane = _planes[outerIndex];
+ if (outerPlane->_priorityChanged) {
+ --outerPlane->_priorityChanged;
+
+ Plane *visibleOuterPlane = _visiblePlanes.findByObject(outerPlane->_object);
+
+ rectlist.add(outerPlane->_screenRect.findIntersectingRect(visibleOuterPlane->_screenRect));
+
+ for (int innerIndex = planeCount - 1; innerIndex >= 0; --innerIndex) {
+ // "inner" just refers to the inner loop
+ Plane *innerPlane = _planes[innerIndex];
+ Plane *visibleInnerPlane = _visiblePlanes.findByObject(innerPlane->_object);
+
+ int rectCount = rectlist.size();
+ for (int rectIndex = 0; rectIndex < rectCount; ++rectIndex) {
+ int splitCount = splitRects(*rectlist[rectIndex], _planes[innerIndex]->_screenRect, outRects);
+
+ if (splitCount == 0) {
+ if (visibleInnerPlane != nullptr) {
+ // same priority, or relative priority between inner/outer changed
+ if ((visibleOuterPlane->_priority - visibleInnerPlane->_priority) * (outerPlane->_priority - innerPlane->_priority) <= 0) {
+ if (outerPlane->_priority <= innerPlane->_priority) {
+ eraseLists[innerIndex].add(*rectlist[rectIndex]);
+ } else {
+ eraseLists[outerIndex].add(*rectlist[rectIndex]);
+ }
+ }
+ }
+
+ rectlist.erase_at(rectIndex);
+ } else if (splitCount != -1) {
+ for (int i = 0; i < splitCount; ++i) {
+ rectlist.add(outRects[i]);
+ }
+
+ if (visibleInnerPlane != nullptr) {
+ // same priority, or relative priority between inner/outer changed
+ if ((visibleOuterPlane->_priority - visibleInnerPlane->_priority) * (outerPlane->_priority - innerPlane->_priority) <= 0) {
+ *rectlist[rectIndex] = outerPlane->_screenRect.findIntersectingRect(innerPlane->_screenRect);
+ if (outerPlane->_priority <= innerPlane->_priority) {
+ eraseLists[innerIndex].add(*rectlist[rectIndex]);
+ }
+ else {
+ eraseLists[outerIndex].add(*rectlist[rectIndex]);
+ }
+ }
+ }
+ rectlist.erase_at(rectIndex);
+ }
+ }
+ rectlist.pack();
+ }
+ }
+ }
- // Add following cels to the itemlist
- FrameoutEntry *picEntry = pictureIt->pictureCels;
- int planePictureCels = planePicture->getSci32celCount();
- for (int pictureCelNr = 0; pictureCelNr < planePictureCels; pictureCelNr++) {
- picEntry->celNo = pictureCelNr;
- picEntry->object = NULL_REG;
- picEntry->picture = planePicture;
- picEntry->y = planePicture->getSci32celY(pictureCelNr);
- picEntry->x = planePicture->getSci32celX(pictureCelNr);
- picEntry->picStartX = pictureIt->startX;
- picEntry->picStartY = pictureIt->startY;
- picEntry->visible = true;
+ for (int planeIndex = 0; planeIndex < planeCount; ++planeIndex) {
+ Plane *plane = _planes[planeIndex];
+ Plane *visiblePlane = nullptr;
- picEntry->priority = planePicture->getSci32celPriority(pictureCelNr);
+ PlaneList::iterator visiblePlaneIt = Common::find_if(_visiblePlanes.begin(), _visiblePlanes.end(), FindByObject<Plane *>(plane->_object));
+ if (visiblePlaneIt != _visiblePlanes.end()) {
+ visiblePlane = *visiblePlaneIt;
+ }
- itemList.push_back(picEntry);
- picEntry++;
+ if (plane->_redrawAllCount) {
+ plane->redrawAll(visiblePlane, _planes, drawLists[planeIndex], eraseLists[planeIndex]);
+ } else {
+ if (visiblePlane == nullptr) {
+ error("Missing visible plane for source plane %04x:%04x", PRINT_REG(plane->_object));
}
+
+ plane->calcLists(*visiblePlane, _planes, drawLists[planeIndex], eraseLists[planeIndex]);
+ }
+
+ if (plane->_created) {
+ _visiblePlanes.add(new Plane(*plane));
+ --plane->_created;
+ } else if (plane->_moved) {
+ assert(visiblePlaneIt != _visiblePlanes.end());
+ **visiblePlaneIt = *plane;
+ --plane->_moved;
}
}
- // Now sort our itemlist
- Common::sort(itemList.begin(), itemList.end(), sortHelper);
-}
+ if (foundTransparentPlane) {
+ for (int planeIndex = 0; planeIndex < planeCount; ++planeIndex) {
+ for (int i = planeIndex + 1; i < planeCount; ++i) {
+ if (_planes[i]->_type == kPlaneTypeTransparent) {
+ _planes[i]->filterUpEraseRects(drawLists[i], eraseLists[planeIndex]);
+ }
+ }
-bool GfxFrameout::isPictureOutOfView(FrameoutEntry *itemEntry, Common::Rect planeRect, int16 planeOffsetX, int16 planeOffsetY) {
- // Out of view horizontally (sanity checks)
- int16 pictureCelStartX = itemEntry->picStartX + itemEntry->x;
- int16 pictureCelEndX = pictureCelStartX + itemEntry->picture->getSci32celWidth(itemEntry->celNo);
- int16 planeStartX = planeOffsetX;
- int16 planeEndX = planeStartX + planeRect.width();
- if (pictureCelEndX < planeStartX)
- return true;
- if (pictureCelStartX > planeEndX)
- return true;
+ if (_planes[planeIndex]->_type == kPlaneTypeTransparent) {
+ for (int i = planeIndex - 1; i >= 0; --i) {
+ _planes[i]->filterDownEraseRects(drawLists[i], eraseLists[i], eraseLists[planeIndex]);
+ }
- // Out of view vertically (sanity checks)
- int16 pictureCelStartY = itemEntry->picStartY + itemEntry->y;
- int16 pictureCelEndY = pictureCelStartY + itemEntry->picture->getSci32celHeight(itemEntry->celNo);
- int16 planeStartY = planeOffsetY;
- int16 planeEndY = planeStartY + planeRect.height();
- if (pictureCelEndY < planeStartY)
- return true;
- if (pictureCelStartY > planeEndY)
- return true;
+ if (eraseLists[planeIndex].size() > 0) {
+ error("Transparent plane's erase list not absorbed");
+ }
+ }
- return false;
+ for (int i = planeIndex + 1; i < planeCount; ++i) {
+ if (_planes[i]->_type == kPlaneTypeTransparent) {
+ _planes[i]->filterUpDrawRects(drawLists[i], drawLists[planeIndex]);
+ }
+ }
+ }
+ }
}
-void GfxFrameout::drawPicture(FrameoutEntry *itemEntry, int16 planeOffsetX, int16 planeOffsetY, bool planePictureMirrored) {
- int16 pictureOffsetX = planeOffsetX;
- int16 pictureX = itemEntry->x;
- if ((planeOffsetX) || (itemEntry->picStartX)) {
- if (planeOffsetX <= itemEntry->picStartX) {
- pictureX += itemEntry->picStartX - planeOffsetX;
- pictureOffsetX = 0;
- } else {
- pictureOffsetX = planeOffsetX - itemEntry->picStartX;
- }
+void GfxFrameout::drawEraseList(const RectList &eraseList, const Plane &plane) {
+ if (plane._type != kPlaneTypeColored) {
+ return;
}
- int16 pictureOffsetY = planeOffsetY;
- int16 pictureY = itemEntry->y;
- if ((planeOffsetY) || (itemEntry->picStartY)) {
- if (planeOffsetY <= itemEntry->picStartY) {
- pictureY += itemEntry->picStartY - planeOffsetY;
- pictureOffsetY = 0;
- } else {
- pictureOffsetY = planeOffsetY - itemEntry->picStartY;
+ for (RectList::const_iterator it = eraseList.begin(); it != eraseList.end(); ++it) {
+ mergeToShowList(**it, _showList, _overdrawThreshold);
+ _currentBuffer.fillRect(**it, plane._back);
+ }
+}
+
+void GfxFrameout::drawScreenItemList(const DrawList &screenItemList) {
+ for (DrawList::const_iterator it = screenItemList.begin(); it != screenItemList.end(); ++it) {
+ DrawItem &drawItem = **it;
+ mergeToShowList(drawItem.rect, _showList, _overdrawThreshold);
+ ScreenItem &screenItem = *drawItem.screenItem;
+ // TODO: Remove
+// debug("Drawing item %04x:%04x to %d %d %d %d", PRINT_REG(screenItem._object), PRINT_RECT(drawItem.rect));
+ CelObj &celObj = *screenItem._celObj;
+ celObj.draw(_currentBuffer, screenItem, drawItem.rect, screenItem._mirrorX ^ celObj._mirrorX);
+ }
+}
+
+void GfxFrameout::mergeToShowList(const Common::Rect &drawRect, RectList &showList, const int overdrawThreshold) {
+ Common::Rect merged(drawRect);
+
+ bool didDelete = true;
+ RectList::size_type count = showList.size();
+ while (didDelete && count) {
+ didDelete = false;
+
+ for (RectList::size_type i = 0; i < count; ++i) {
+ Common::Rect existing = *showList[i];
+ Common::Rect candidate;
+ candidate.left = MIN(merged.left, existing.left);
+ candidate.top = MIN(merged.top, existing.top);
+ candidate.right = MAX(merged.right, existing.right);
+ candidate.bottom = MAX(merged.bottom, existing.bottom);
+
+ if (candidate.height() * candidate.width() - merged.width() * merged.height() - existing.width() * existing.height() <= overdrawThreshold) {
+ merged = candidate;
+ showList.erase_at(i);
+ didDelete = true;
+ }
}
+
+ count = showList.pack();
}
- itemEntry->picture->drawSci32Vga(itemEntry->celNo, pictureX, itemEntry->y, pictureOffsetX, pictureOffsetY, planePictureMirrored);
- // warning("picture cel %d %d", itemEntry->celNo, itemEntry->priority);
+ showList.add(merged);
}
-/* TODO: This is the proper implementation of GraphicsMgr::FrameOut transcribed from SQ6 SCI engine disassembly.
-static DrawList* g_drawLists[100];
-static RectList* g_rectLists[100];
-void GfxFrameout::FrameOut(bool shouldShowBits, SOL_Rect *rect) {
- if (robot) {
- robot.doRobot();
+void GfxFrameout::palMorphFrameOut(const int8 *styleRanges, const ShowStyleEntry *showStyle) {
+ Palette sourcePalette(*_palette->getNextPalette());
+ alterVmap(sourcePalette, sourcePalette, -1, styleRanges);
+
+ int16 prevRoom = g_sci->getEngineState()->variables[VAR_GLOBAL][12].toSint16();
+
+ Common::Rect rect(_screen->getDisplayWidth(), _screen->getDisplayHeight());
+ _showList.add(rect);
+ showBits();
+
+ Common::Rect calcRect(0, 0);
+
+ // NOTE: The original engine allocated these as static arrays of 100
+ // pointers to ScreenItemList / RectList
+ ScreenItemListList screenItemLists;
+ EraseListList eraseLists;
+
+ screenItemLists.resize(_planes.size());
+ eraseLists.resize(_planes.size());
+
+ if (g_sci->_gfxRemap32->getRemapCount() > 0 && _remapOccurred) {
+ remapMarkRedraw();
}
- auto planeCount = screen.planeList.planeCount;
- if (planeCount > 0) {
- for (int planeIndex = 0; planeIndex < planeCount; ++planeIndex) {
- Plane plane = *screen.planeList[planeIndex];
+ calcLists(screenItemLists, eraseLists, calcRect);
+ for (ScreenItemListList::iterator list = screenItemLists.begin(); list != screenItemLists.end(); ++list) {
+ list->sort();
+ }
- DrawList* drawList = new DrawList();
- g_drawLists[planeIndex] = drawList;
- RectList* rectList = new RectList();
- g_rectLists[planeIndex] = rectList;
+ for (ScreenItemListList::iterator list = screenItemLists.begin(); list != screenItemLists.end(); ++list) {
+ for (DrawList::iterator drawItem = list->begin(); drawItem != list->end(); ++drawItem) {
+ (*drawItem)->screenItem->getCelObj().submitPalette();
}
}
-
- if (g_Remap_numActiveRemaps > 0 && remapNeeded) {
- screen.RemapMarkRedraw();
+
+ _remapOccurred = _palette->updateForFrame();
+ _frameNowVisible = false;
+
+ for (PlaneList::size_type i = 0; i < _planes.size(); ++i) {
+ drawEraseList(eraseLists[i], *_planes[i]);
+ drawScreenItemList(screenItemLists[i]);
}
-
- CalcLists(&g_drawLists, &g_rectLists, rect);
- // SCI engine stores reference *after* CalcLists
- planeCount = screen.planeList.planeCount;
- if (planeCount > 0) {
- for (int drawListIndex = 0; drawListIndex < planeCount; ++i) {
- DrawList* drawList = g_drawLists[drawListIndex];
- drawList->Sort();
- }
+ Palette nextPalette(*_palette->getNextPalette());
- for (int drawListIndex = 0; drawListIndex < planeCount; ++i) {
- DrawList* drawList = g_drawLists[drawListIndex];
- if (drawList == nullptr || drawList->count == 0) {
- continue;
+ if (prevRoom < 1000) {
+ for (int i = 0; i < ARRAYSIZE(sourcePalette.colors); ++i) {
+ if (styleRanges[i] == -1 || styleRanges[i] == 0) {
+ sourcePalette.colors[i] = nextPalette.colors[i];
+ sourcePalette.colors[i].used = true;
}
-
- for (int screenItemIndex = 0, screenItemCount = drawList->count; screenItemIndex < screenItemCount; ++screenItemIndex) {
- ScreenItem* screenItem = drawList->items[screenItemIndex];
- screenItem->GetCelObj()->SubmitPalette();
+ }
+ } else {
+ for (int i = 0; i < ARRAYSIZE(sourcePalette.colors); ++i) {
+ if (styleRanges[i] == -1 || (styleRanges[i] == 0 && i > 71 && i < 104)) {
+ sourcePalette.colors[i] = nextPalette.colors[i];
+ sourcePalette.colors[i].used = true;
}
}
}
- // UpdateForFrame is where all palette mutations occur (cycles, varies, etc.)
- bool remapNeeded = GPalette().UpdateForFrame();
- if (planeCount > 0) {
- frameNowVisible = false;
+ _palette->submit(sourcePalette);
+ _palette->updateFFrame();
+ _palette->updateHardware();
+ alterVmap(nextPalette, sourcePalette, 1, _styleRanges);
- for (int planeIndex = 0; planeIndex < planeCount; ++planeIndex) {
- Plane* plane = screen.planeList[planeIndex];
+ if (showStyle && showStyle->type != kShowStyleUnknown) {
+// TODO: SCI2.1mid transition effects
+// processEffects();
+ warning("Transition %d not implemented!", showStyle->type);
+ } else {
+ showBits();
+ }
+
+ _frameNowVisible = true;
- DrawEraseList(g_rectLists[planeIndex], plane);
- DrawScreenItemsList(g_drawLists[planeIndex]);
+ for (PlaneList::iterator plane = _planes.begin(); plane != _planes.end(); ++plane) {
+ (*plane)->_redrawAllCount = getScreenCount();
+ }
+
+ if (g_sci->_gfxRemap32->getRemapCount() > 0 && _remapOccurred) {
+ remapMarkRedraw();
+ }
+
+ calcLists(screenItemLists, eraseLists, calcRect);
+ for (ScreenItemListList::iterator list = screenItemLists.begin(); list != screenItemLists.end(); ++list) {
+ list->sort();
+ }
+
+ for (ScreenItemListList::iterator list = screenItemLists.begin(); list != screenItemLists.end(); ++list) {
+ for (DrawList::iterator drawItem = list->begin(); drawItem != list->end(); ++drawItem) {
+ (*drawItem)->screenItem->getCelObj().submitPalette();
}
}
- if (robot) {
- robot.FrameAlmostVisible();
+ _remapOccurred = _palette->updateForFrame();
+ // NOTE: During this second loop, `_frameNowVisible = false` is
+ // inside the next loop in SCI2.1mid
+ _frameNowVisible = false;
+
+ for (PlaneList::size_type i = 0; i < _planes.size(); ++i) {
+ drawEraseList(eraseLists[i], *_planes[i]);
+ drawScreenItemList(screenItemLists[i]);
}
- GPalette().UpdateHardware();
+ _palette->submit(nextPalette);
+ _palette->updateFFrame();
+ _palette->updateHardware();
+ showBits();
- if (shouldShowBits) {
- ShowBits();
+ _frameNowVisible = true;
+}
+
+// TODO: What does the bit masking for the show rects do,
+// and does it cause an off-by-one error in rect calculations
+// since SOL_Rect is BR inclusive and Common::Rect is BR
+// exclusive?
+void GfxFrameout::showBits() {
+ for (RectList::const_iterator rect = _showList.begin(); rect != _showList.end(); ++rect) {
+ Common::Rect rounded(**rect);
+ // NOTE: SCI engine used BR-inclusive rects so used slightly
+ // different masking here to ensure that the width of rects
+ // was always even.
+ rounded.left &= ~1;
+ rounded.right = (rounded.right + 1) & ~1;
+
+ // TODO:
+ // _cursor->GonnaPaint(rounded);
}
- frameNowVisible = true;
+ // TODO:
+ // _cursor->PaintStarting();
+
+ for (RectList::const_iterator rect = _showList.begin(); rect != _showList.end(); ++rect) {
+ Common::Rect rounded(**rect);
+ // NOTE: SCI engine used BR-inclusive rects so used slightly
+ // different masking here to ensure that the width of rects
+ // was always even.
+ rounded.left &= ~1;
+ rounded.right = (rounded.right + 1) & ~1;
- if (robot) {
- robot.FrameNowVisible();
+ byte *sourceBuffer = (byte *)_currentBuffer.getPixels() + rounded.top * _currentBuffer.screenWidth + rounded.left;
+
+ g_system->copyRectToScreen(sourceBuffer, _currentBuffer.screenWidth, rounded.left, rounded.top, rounded.width(), rounded.height());
}
- if (planeCount > 0) {
- for (int planeIndex = 0; planeIndex < planeCount; ++planeIndex) {
- if (g_rectLists[planeIndex] != nullptr) {
- delete g_rectLists[planeIndex];
+ // TODO:
+ // _cursor->DonePainting();
+
+ _showList.clear();
+}
+
+void GfxFrameout::alterVmap(const Palette &palette1, const Palette &palette2, const int8 style, const int8 *const styleRanges) {
+ uint8 clut[256];
+
+ for (int paletteIndex = 0; paletteIndex < ARRAYSIZE(palette1.colors); ++paletteIndex) {
+ int outerR = palette1.colors[paletteIndex].r;
+ int outerG = palette1.colors[paletteIndex].g;
+ int outerB = palette1.colors[paletteIndex].b;
+
+ if (styleRanges[paletteIndex] == style) {
+ int minDiff = 262140;
+ int minDiffIndex;
+
+ for (int i = 0; i < 236; ++i) {
+ if (styleRanges[i] != style) {
+ int r = palette1.colors[i].r;
+ int g = palette1.colors[i].g;
+ int b = palette1.colors[i].b;
+ int diffSquared = (outerR - r) * (outerR - r) + (outerG - g) * (outerG - g) + (outerB - b) * (outerB - b);
+ if (diffSquared < minDiff) {
+ minDiff = diffSquared;
+ minDiffIndex = i;
+ }
+ }
}
- if (g_drawLists[planeIndex] != nullptr) {
- delete g_drawLists[planeIndex];
+
+ clut[paletteIndex] = minDiffIndex;
+ }
+
+ if (style == 1 && styleRanges[paletteIndex] == 0) {
+ int minDiff = 262140;
+ int minDiffIndex;
+
+ for (int i = 0; i < 236; ++i) {
+ int r = palette2.colors[i].r;
+ int g = palette2.colors[i].g;
+ int b = palette2.colors[i].b;
+
+ int diffSquared = (outerR - r) * (outerR - r) + (outerG - g) * (outerG - g) + (outerB - b) * (outerB - b);
+ if (diffSquared < minDiff) {
+ minDiff = diffSquared;
+ minDiffIndex = i;
+ }
}
+
+ clut[paletteIndex] = minDiffIndex;
+ }
+ }
+
+ // NOTE: This is currBuffer->ptr in SCI engine
+ byte *pixels = (byte *)_currentBuffer.getPixels();
+
+ for (int pixelIndex = 0, numPixels = _currentBuffer.screenWidth * _currentBuffer.screenHeight; pixelIndex < numPixels; ++pixelIndex) {
+ byte currentValue = pixels[pixelIndex];
+ int8 styleRangeValue = styleRanges[currentValue];
+ if (styleRangeValue == -1 && styleRangeValue == style) {
+ currentValue = pixels[pixelIndex] = clut[currentValue];
+ // NOTE: In original engine this assignment happens outside of the
+ // condition, but if the branch is not followed the value is just
+ // going to be the same as it was before
+ styleRangeValue = styleRanges[currentValue];
+ }
+
+ if (
+ (styleRangeValue == 1 && styleRangeValue == style) ||
+ (styleRangeValue == 0 && style == 1)
+ ) {
+ pixels[pixelIndex] = clut[currentValue];
}
}
}
-void GfxFrameout::CalcLists(DrawList **drawLists, RectList **rectLists, SOL_Rect *rect) {
- screen.CalcLists(&visibleScreen, drawLists, rectLists, rect);
-}
-*/
-void GfxFrameout::kernelFrameout() {
- if (g_sci->_robotDecoder->isVideoLoaded()) {
- showVideo();
+
+void GfxFrameout::kernelSetPalStyleRange(const uint8 fromColor, const uint8 toColor) {
+ if (toColor > fromColor) {
return;
}
- _palette->updateForFrame();
+ for (int i = fromColor; i < toColor; ++i) {
+ _styleRanges[i] = 0;
+ }
+}
- // TODO: Tons of drawing stuff should be here, see commented out implementation above
+inline ShowStyleEntry * GfxFrameout::findShowStyleForPlane(const reg_t planeObj) const {
+ ShowStyleEntry *entry = _showStyles;
+ while (entry != nullptr) {
+ if (entry->plane == planeObj) {
+ break;
+ }
+ entry = entry->next;
+ }
- _palette->updateHardware();
+ return entry;
+}
- for (PlaneList::iterator it = _planes.begin(); it != _planes.end(); it++) {
- reg_t planeObject = it->object;
+inline ShowStyleEntry *GfxFrameout::deleteShowStyleInternal(ShowStyleEntry *const showStyle) {
+ ShowStyleEntry *lastEntry = nullptr;
- // Draw any plane lines, if they exist
- // These are drawn on invisible planes as well. (e.g. "invisiblePlane" in LSL6 hires)
- // FIXME: Lines aren't always drawn (e.g. when the narrator speaks in LSL6 hires).
- // Perhaps something is painted over them?
- for (PlaneLineList::iterator it2 = it->lines.begin(); it2 != it->lines.end(); ++it2) {
- Common::Point startPoint = it2->startPoint;
- Common::Point endPoint = it2->endPoint;
- _coordAdjuster->kernelLocalToGlobal(startPoint.x, startPoint.y, it->object);
- _coordAdjuster->kernelLocalToGlobal(endPoint.x, endPoint.y, it->object);
- _screen->drawLine(startPoint, endPoint, it2->color, it2->priority, it2->control);
+ for (ShowStyleEntry *testEntry = _showStyles; testEntry != nullptr; testEntry = testEntry->next) {
+ if (testEntry == showStyle) {
+ break;
}
+ lastEntry = testEntry;
+ }
- int16 planeLastPriority = it->lastPriority;
+ if (lastEntry == nullptr) {
+ _showStyles = showStyle->next;
+ lastEntry = _showStyles;
+ } else {
+ lastEntry->next = showStyle->next;
+ }
- // Update priority here, sq6 sets it w/o UpdatePlane
- int16 planePriority = it->priority = readSelectorValue(_segMan, planeObject, SELECTOR(priority));
+ delete[] showStyle->fadeColorRanges;
+ delete showStyle;
- it->lastPriority = planePriority;
- if (planePriority < 0) { // Plane currently not meant to be shown
- // If plane was shown before, delete plane rect
- if (planePriority != planeLastPriority)
- _paint32->fillRect(it->planeRect, 0);
- continue;
- }
+ // TODO: Verify that this is the correct entry to return
+ // for the loop in processShowStyles to work correctly
+ return lastEntry;
+}
- // There is a race condition lurking in SQ6, which causes the game to hang in the intro, when teleporting to Polysorbate LX.
- // Since I first wrote the patch, the race has stopped occurring for me though.
- // I'll leave this for investigation later, when someone can reproduce.
- //if (it->pictureId == kPlanePlainColored) // FIXME: This is what SSCI does, and fixes the intro of LSL7, but breaks the dialogs in GK1 (adds black boxes)
- if (it->pictureId == kPlanePlainColored && (it->planeBack || g_sci->getGameId() != GID_GK1))
- _paint32->fillRect(it->planeRect, it->planeBack);
+// TODO: 10-argument version is only in SCI3; argc checks are currently wrong for this version
+// and need to be fixed in future
+// TODO: SQ6 does not use 'priority' (exists since SCI2) or 'blackScreen' (exists since SCI3);
+// check to see if other versions use or if they are just always ignored
+void GfxFrameout::kernelSetShowStyle(const uint16 argc, const reg_t planeObj, const ShowStyleType type, const int16 seconds, const int16 back, const int16 priority, const int16 animate, const int16 frameOutNow, reg_t pFadeArray, int16 divisions, const int16 blackScreen) {
+
+ bool hasDivisions = false;
+ bool hasFadeArray = false;
+
+ // KQ7 2.0b uses a mismatched version of the Styler script (SCI2.1early script
+ // for SCI2.1mid engine), so the calls it makes to kSetShowStyle are wrong and
+ // put `divisions` where `pFadeArray` is supposed to be
+ if (getSciVersion() == SCI_VERSION_2_1_MIDDLE && g_sci->getGameId() == GID_KQ7) {
+ hasDivisions = argc > 7;
+ hasFadeArray = false;
+ divisions = argc > 7 ? pFadeArray.toSint16() : -1;
+ pFadeArray = NULL_REG;
+ } else if (getSciVersion() < SCI_VERSION_2_1_MIDDLE) {
+ hasDivisions = argc > 7;
+ hasFadeArray = false;
+ } else if (getSciVersion() < SCI_VERSION_3) {
+ hasDivisions = argc > 8;
+ hasFadeArray = argc > 7;
+ } else {
+ hasDivisions = argc > 9;
+ hasFadeArray = argc > 8;
+ }
- _coordAdjuster->pictureSetDisplayArea(it->planeRect);
- // Invoking drewPicture() with an invalid picture ID in SCI32 results in
- // invalidating the palVary palette when a palVary effect is active. This
- // is quite obvious in QFG4, where the day time palette is incorrectly
- // shown when exiting the caves, and the correct night time palette
- // flashes briefly each time that kPalVaryInit is called.
- if (it->pictureId != 0xFFFF)
- _palette->drewPicture(it->pictureId);
+ bool isFadeUp;
+ int16 color;
+ if (back != -1) {
+ isFadeUp = false;
+ color = back;
+ } else {
+ isFadeUp = true;
+ color = 0;
+ }
- FrameoutList itemList;
+ if ((getSciVersion() < SCI_VERSION_2_1_MIDDLE && type == 15) || type > 15) {
+ error("Illegal show style %d for plane %04x:%04x", type, PRINT_REG(planeObj));
+ }
- createPlaneItemList(planeObject, itemList);
+ Plane *plane = _planes.findByObject(planeObj);
+ if (plane == nullptr) {
+ error("Plane %04x:%04x is not present in active planes list", PRINT_REG(planeObj));
+ }
- for (FrameoutList::iterator listIterator = itemList.begin(); listIterator != itemList.end(); listIterator++) {
- FrameoutEntry *itemEntry = *listIterator;
+ bool createNewEntry = true;
+ ShowStyleEntry *entry = findShowStyleForPlane(planeObj);
+ if (entry != nullptr) {
+ // TODO: SCI2.1early has different criteria for show style reuse
+ bool useExisting = true;
- if (!itemEntry->visible)
- continue;
+ if (useExisting) {
+ useExisting = entry->divisions == (hasDivisions ? divisions : _defaultDivisions[type]) && entry->unknownC == _defaultUnknownC[type];
+ }
- if (itemEntry->object.isNull()) {
- // Picture cel data
- _coordAdjuster->fromScriptToDisplay(itemEntry->y, itemEntry->x);
- _coordAdjuster->fromScriptToDisplay(itemEntry->picStartY, itemEntry->picStartX);
+ if (useExisting) {
+ createNewEntry = false;
+ isFadeUp = true;
+ entry->currentStep = 0;
+ } else {
+ isFadeUp = true;
+ color = entry->color;
+ deleteShowStyleInternal(entry/*, true*/);
+ entry = nullptr;
+ }
+ }
- if (!isPictureOutOfView(itemEntry, it->planeRect, it->planeOffsetX, it->planeOffsetY))
- drawPicture(itemEntry, it->planeOffsetX, it->planeOffsetY, it->planePictureMirrored);
- } else {
- GfxView *view = (itemEntry->viewId != 0xFFFF) ? _cache->getView(itemEntry->viewId) : NULL;
- int16 dummyX = 0;
-
- if (view && view->isSci2Hires()) {
- view->adjustToUpscaledCoordinates(itemEntry->y, itemEntry->x);
- view->adjustToUpscaledCoordinates(itemEntry->z, dummyX);
- } else if (getSciVersion() >= SCI_VERSION_2_1_EARLY) {
- _coordAdjuster->fromScriptToDisplay(itemEntry->y, itemEntry->x);
- _coordAdjuster->fromScriptToDisplay(itemEntry->z, dummyX);
+ if (type > 0) {
+ if (createNewEntry) {
+ entry = new ShowStyleEntry;
+ // NOTE: SCI2.1 engine tests if allocation returned a null pointer
+ // but then only avoids setting currentStep if this is so. Since
+ // this is a nonsensical approach, we do not do that here
+ entry->currentStep = 0;
+ entry->unknownC = _defaultUnknownC[type];
+ entry->processed = false;
+ entry->divisions = hasDivisions ? divisions : _defaultDivisions[type];
+ entry->plane = planeObj;
+
+ entry->fadeColorRanges = nullptr;
+ if (hasFadeArray) {
+ // NOTE: SCI2.1mid engine does no check to verify that an array is
+ // successfully retrieved, and SegMan will cause a fatal error
+ // if we try to use a memory segment that is not an array
+ SciArray<reg_t> *table = _segMan->lookupArray(pFadeArray);
+
+ uint32 rangeCount = table->getSize();
+ entry->fadeColorRangesCount = rangeCount;
+
+ // NOTE: SCI engine code always allocates memory even if the range
+ // table has no entries, but this does not really make sense, so
+ // we avoid the allocation call in this case
+ if (rangeCount > 0) {
+ entry->fadeColorRanges = new uint16[rangeCount];
+ for (size_t i = 0; i < rangeCount; ++i) {
+ entry->fadeColorRanges[i] = table->getValue(i).toUint16();
+ }
}
+ } else {
+ entry->fadeColorRangesCount = 0;
+ }
+ }
- // Adjust according to current scroll position
- itemEntry->x -= it->planeOffsetX;
- itemEntry->y -= it->planeOffsetY;
-
- uint16 useInsetRect = readSelectorValue(_segMan, itemEntry->object, SELECTOR(useInsetRect));
- if (useInsetRect) {
- itemEntry->celRect.top = readSelectorValue(_segMan, itemEntry->object, SELECTOR(inTop));
- itemEntry->celRect.left = readSelectorValue(_segMan, itemEntry->object, SELECTOR(inLeft));
- itemEntry->celRect.bottom = readSelectorValue(_segMan, itemEntry->object, SELECTOR(inBottom));
- itemEntry->celRect.right = readSelectorValue(_segMan, itemEntry->object, SELECTOR(inRight));
- if (view && view->isSci2Hires()) {
- view->adjustToUpscaledCoordinates(itemEntry->celRect.top, itemEntry->celRect.left);
- view->adjustToUpscaledCoordinates(itemEntry->celRect.bottom, itemEntry->celRect.right);
- }
- itemEntry->celRect.translate(itemEntry->x, itemEntry->y);
- // TODO: maybe we should clip the cels rect with this, i'm not sure
- // the only currently known usage is game menu of gk1
- } else if (view) {
- // Process global scaling, if needed.
- // TODO: Seems like SCI32 always processes global scaling for scaled objects
- // TODO: We can only process symmetrical scaling for now (i.e. same value for scaleX/scaleY)
- if ((itemEntry->scaleSignal & kScaleSignalDoScaling32) &&
- !(itemEntry->scaleSignal & kScaleSignalDisableGlobalScaling32) &&
- (itemEntry->scaleX == itemEntry->scaleY) &&
- itemEntry->scaleX != 128)
- applyGlobalScaling(itemEntry, it->planeRect, view->getHeight(itemEntry->loopNo, itemEntry->celNo));
-
- if ((itemEntry->scaleX == 128) && (itemEntry->scaleY == 128))
- view->getCelRect(itemEntry->loopNo, itemEntry->celNo,
- itemEntry->x, itemEntry->y, itemEntry->z, itemEntry->celRect);
- else
- view->getCelScaledRect(itemEntry->loopNo, itemEntry->celNo,
- itemEntry->x, itemEntry->y, itemEntry->z, itemEntry->scaleX,
- itemEntry->scaleY, itemEntry->celRect);
-
- Common::Rect nsRect = itemEntry->celRect;
- // Translate back to actual coordinate within scrollable plane
- nsRect.translate(it->planeOffsetX, it->planeOffsetY);
-
- if (g_sci->getGameId() == GID_PHANTASMAGORIA2) {
- // HACK: Some (?) objects in Phantasmagoria 2 have no NS rect. Skip them for now.
- // TODO: Remove once we figure out how Phantasmagoria 2 draws objects on screen.
- if (lookupSelector(_segMan, itemEntry->object, SELECTOR(nsLeft), NULL, NULL) != kSelectorVariable)
- continue;
- }
+ // NOTE: The original engine had no nullptr check and would just crash
+ // if it got to here
+ if (entry == nullptr) {
+ error("Cannot edit non-existing ShowStyle entry");
+ }
- if (view && view->isSci2Hires()) {
- view->adjustBackUpscaledCoordinates(nsRect.top, nsRect.left);
- view->adjustBackUpscaledCoordinates(nsRect.bottom, nsRect.right);
- g_sci->_gfxCompare->setNSRect(itemEntry->object, nsRect);
- } else if (getSciVersion() >= SCI_VERSION_2_1_EARLY && _isHiRes) {
- _coordAdjuster->fromDisplayToScript(nsRect.top, nsRect.left);
- _coordAdjuster->fromDisplayToScript(nsRect.bottom, nsRect.right);
- g_sci->_gfxCompare->setNSRect(itemEntry->object, nsRect);
- }
+ entry->fadeUp = isFadeUp;
+ entry->color = color;
+ entry->nextTick = g_sci->getTickCount();
+ entry->type = type;
+ entry->animate = animate;
+ entry->delay = (seconds * 60 + entry->divisions - 1) / entry->divisions;
- // TODO: For some reason, the top left nsRect coordinates get
- // swapped in the GK1 inventory screen, investigate why.
- // This is also needed for GK1 rooms 710 and 720 (catacombs, inner and
- // outer circle), for handling the tiles and talking to Wolfgang.
- // HACK: Fix the coordinates by explicitly setting them here for GK1.
- // Also check bug #6729, for another case where this is needed.
- if (g_sci->getGameId() == GID_GK1)
- g_sci->_gfxCompare->setNSRect(itemEntry->object, nsRect);
- }
+ if (entry->delay == 0) {
+ if (entry->fadeColorRanges != nullptr) {
+ delete[] entry->fadeColorRanges;
+ }
+ delete entry;
+ error("ShowStyle has no duration");
+ }
- // Don't attempt to draw sprites that are outside the visible
- // screen area. An example is the random people walking in
- // Jackson Square in GK1.
- if (itemEntry->celRect.bottom < 0 || itemEntry->celRect.top >= _screen->getDisplayHeight() ||
- itemEntry->celRect.right < 0 || itemEntry->celRect.left >= _screen->getDisplayWidth())
- continue;
-
- Common::Rect clipRect, translatedClipRect;
- clipRect = itemEntry->celRect;
-
- if (view && view->isSci2Hires()) {
- clipRect.clip(it->upscaledPlaneClipRect);
- translatedClipRect = clipRect;
- translatedClipRect.translate(it->upscaledPlaneRect.left, it->upscaledPlaneRect.top);
- } else {
- // QFG4 passes invalid rectangles when a battle is starting
- if (!clipRect.isValidRect())
- continue;
- clipRect.clip(it->planeClipRect);
- translatedClipRect = clipRect;
- translatedClipRect.translate(it->planeRect.left, it->planeRect.top);
- }
+ if (frameOutNow) {
+ Common::Rect frameOutRect(0, 0);
+ frameOut(false, frameOutRect);
+ }
+
+ if (createNewEntry) {
+ // TODO: Implement SCI2.1early and SCI3
+ entry->next = _showStyles;
+ _showStyles = entry;
+ }
+ }
+}
- if (view) {
- if (!clipRect.isEmpty()) {
- if ((itemEntry->scaleX == 128) && (itemEntry->scaleY == 128))
- view->draw(itemEntry->celRect, clipRect, translatedClipRect,
- itemEntry->loopNo, itemEntry->celNo, 255, 0, view->isSci2Hires());
- else
- view->drawScaled(itemEntry->celRect, clipRect, translatedClipRect,
- itemEntry->loopNo, itemEntry->celNo, 255, itemEntry->scaleX, itemEntry->scaleY);
+// NOTE: Different version of SCI engine support different show styles
+// SCI2 implements 0, 1/3/5/7/9, 2/4/6/8/10, 11, 12, 13, 14
+// SCI2.1 implements 0, 1/2/3/4/5/6/7/8/9/10/11/12/15, 13, 14
+// SCI3 implements 0, 1/3/5/7/9, 2/4/6/8/10, 11, 12/15, 13, 14
+// TODO: Sierra code needs to be replaced with code that uses the
+// computed entry->delay property instead of just counting divisors,
+// as the latter is machine-speed-dependent and leads to wrong
+// transition speeds
+void GfxFrameout::processShowStyles() {
+ uint32 now = g_sci->getTickCount();
+
+ bool continueProcessing;
+
+ // TODO: Change to bool? Engine uses inc to set the value to true,
+ // but there does not seem to be any reason to actually count how
+ // many times it was set
+ int doFrameOut;
+ do {
+ continueProcessing = false;
+ doFrameOut = 0;
+ ShowStyleEntry *showStyle = _showStyles;
+ while (showStyle != nullptr) {
+ bool retval = false;
+
+ if (!showStyle->animate) {
+ ++doFrameOut;
+ }
+
+ if (showStyle->nextTick < now || !showStyle->animate) {
+ // TODO: Different versions of SCI use different processors!
+ // This is the SQ6/KQ7/SCI2.1mid table.
+ switch (showStyle->type) {
+ case kShowStyleNone: {
+ retval = processShowStyleNone(showStyle);
+ break;
+ }
+ case kShowStyleHShutterOut:
+ case kShowStyleVShutterOut:
+ case kShowStyleWipeLeft:
+ case kShowStyleWipeUp:
+ case kShowStyleIrisOut:
+ case kShowStyleHShutterIn:
+ case kShowStyleVShutterIn:
+ case kShowStyleWipeRight:
+ case kShowStyleWipeDown:
+ case kShowStyleIrisIn:
+ case kShowStyle11:
+ case kShowStyle12:
+ case kShowStyleUnknown: {
+ retval = processShowStyleMorph(showStyle);
+ break;
+ }
+ case kShowStyleFadeOut: {
+ retval = processShowStyleFade(-1, showStyle);
+ break;
+ }
+ case kShowStyleFadeIn: {
+ retval = processShowStyleFade(1, showStyle);
+ break;
}
}
+ }
- // Draw text, if it exists
- if (lookupSelector(_segMan, itemEntry->object, SELECTOR(text), NULL, NULL) == kSelectorVariable) {
- g_sci->_gfxText32->drawTextBitmap(itemEntry->x, itemEntry->y, it->planeRect, itemEntry->object);
- }
+ if (!retval) {
+ continueProcessing = true;
}
- }
- for (PlanePictureList::iterator pictureIt = _planePictures.begin(); pictureIt != _planePictures.end(); pictureIt++) {
- if (pictureIt->object == planeObject) {
- delete[] pictureIt->pictureCels;
- pictureIt->pictureCels = 0;
+ if (retval && showStyle->processed) {
+ showStyle = deleteShowStyleInternal(showStyle);
+ } else {
+ showStyle = showStyle->next;
}
}
- }
- showCurrentScrollText();
+ if (doFrameOut) {
+ frameOut(true);
+
+ // TODO: Transitions without the “animate” flag are too
+ // fast, but the throttle value is arbitrary. Someone on
+ // real hardware probably needs to test what the actual
+ // speed of these transitions should be
+ EngineState *state = g_sci->getEngineState();
+ state->speedThrottler(33);
+ state->_throttleTrigger = true;
+ }
+ } while(continueProcessing && doFrameOut);
+}
- _screen->copyToScreen();
+bool GfxFrameout::processShowStyleNone(ShowStyleEntry *const showStyle) {
+ if (showStyle->fadeUp) {
+ _palette->setFade(100, 0, 255);
+ } else {
+ _palette->setFade(0, 0, 255);
+ }
- g_sci->getEngineState()->_throttleTrigger = true;
+ showStyle->processed = true;
+ return true;
}
-void GfxFrameout::printPlaneList(Console *con) {
- for (PlaneList::const_iterator it = _planes.begin(); it != _planes.end(); ++it) {
- PlaneEntry p = *it;
- Common::String curPlaneName = _segMan->getObjectName(p.object);
- Common::Rect r = p.upscaledPlaneRect;
- Common::Rect cr = p.upscaledPlaneClipRect;
-
- con->debugPrintf("%04x:%04x (%s): prio %d, lastprio %d, offsetX %d, offsetY %d, pic %d, mirror %d, back %d\n",
- PRINT_REG(p.object), curPlaneName.c_str(),
- (int16)p.priority, (int16)p.lastPriority,
- p.planeOffsetX, p.planeOffsetY, p.pictureId,
- p.planePictureMirrored, p.planeBack);
- con->debugPrintf(" rect: (%d, %d, %d, %d), clip rect: (%d, %d, %d, %d)\n",
- r.left, r.top, r.right, r.bottom,
- cr.left, cr.top, cr.right, cr.bottom);
-
- if (p.pictureId != 0xffff && p.pictureId != 0xfffe) {
- con->debugPrintf("Pictures:\n");
-
- for (PlanePictureList::iterator pictureIt = _planePictures.begin(); pictureIt != _planePictures.end(); pictureIt++) {
- if (pictureIt->object == p.object) {
- con->debugPrintf(" Picture %d: x %d, y %d\n", pictureIt->pictureId, pictureIt->startX, pictureIt->startY);
- }
+bool GfxFrameout::processShowStyleMorph(ShowStyleEntry *const showStyle) {
+ palMorphFrameOut(_styleRanges, showStyle);
+ showStyle->processed = true;
+ return true;
+}
+
+// TODO: Normalise use of 'entry' vs 'showStyle'
+bool GfxFrameout::processShowStyleFade(const int direction, ShowStyleEntry *const showStyle) {
+ bool unchanged = true;
+ if (showStyle->currentStep < showStyle->divisions) {
+ int percent;
+ if (direction <= 0) {
+ percent = showStyle->divisions - showStyle->currentStep - 1;
+ } else {
+ percent = showStyle->currentStep;
+ }
+
+ percent *= 100;
+ percent /= showStyle->divisions - 1;
+
+ if (showStyle->fadeColorRangesCount > 0) {
+ for (int i = 0, len = showStyle->fadeColorRangesCount; i < len; i += 2) {
+ _palette->setFade(percent, showStyle->fadeColorRanges[i], showStyle->fadeColorRanges[i + 1]);
}
+ } else {
+ _palette->setFade(percent, 0, 255);
}
+
+ ++showStyle->currentStep;
+ showStyle->nextTick += showStyle->delay;
+ unchanged = false;
+ }
+
+ if (showStyle->currentStep >= showStyle->divisions && unchanged) {
+ if (direction > 0) {
+ showStyle->processed = true;
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+void GfxFrameout::kernelFrameOut(const bool shouldShowBits) {
+ if (_showStyles != nullptr) {
+ processShowStyles();
+ } else if (_palMorphIsOn) {
+ palMorphFrameOut(_styleRanges, nullptr);
+ _palMorphIsOn = false;
+ } else {
+// TODO: Window scroll
+// if (g_PlaneScroll) {
+// processScrolls();
+// }
+
+ frameOut(shouldShowBits);
+ }
+}
+
+#pragma mark -
+#pragma mark Mouse cursor
+
+reg_t GfxFrameout::kernelIsOnMe(const reg_t object, const Common::Point &position, bool checkPixel) const {
+ const reg_t planeObject = readSelector(_segMan, object, SELECTOR(plane));
+ Plane *plane = _visiblePlanes.findByObject(planeObject);
+ if (plane == nullptr) {
+ return make_reg(0, 0);
+ }
+
+ ScreenItem *screenItem = plane->_screenItemList.findByObject(object);
+ if (screenItem == nullptr) {
+ return make_reg(0, 0);
}
+
+ // NOTE: The original engine passed a copy of the ScreenItem into isOnMe
+ // as a hack around the fact that the screen items in `_visiblePlanes`
+ // did not have their `_celObj` pointers cleared when their CelInfo was
+ // updated by `Plane::decrementScreenItemArrayCounts`. We handle this
+ // this more intelligently by clearing `_celObj` in the copy assignment
+ // operator, which is only ever called by `decrementScreenItemArrayCounts`
+ // anyway.
+ return make_reg(0, isOnMe(*screenItem, *plane, position, checkPixel));
}
-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));
+bool GfxFrameout::isOnMe(const ScreenItem &screenItem, const Plane &plane, const Common::Point &position, const bool checkPixel) const {
+
+ Common::Point scaledPosition(position);
+ mulru(scaledPosition, Ratio(_currentBuffer.screenWidth, _currentBuffer.scriptWidth), Ratio(_currentBuffer.screenHeight, _currentBuffer.scriptHeight));
+ scaledPosition.x += plane._planeRect.left;
+ scaledPosition.y += plane._planeRect.top;
+
+ if (!screenItem._screenRect.contains(scaledPosition)) {
+ return false;
+ }
+
+ if (checkPixel) {
+ CelObj &celObj = screenItem.getCelObj();
+
+ bool mirrorX = screenItem._mirrorX ^ celObj._mirrorX;
+
+ scaledPosition.x -= screenItem._scaledPosition.x;
+ scaledPosition.y -= screenItem._scaledPosition.y;
- if (planeObject == itemPlane) {
- Common::String curItemName = _segMan->getObjectName(e->object);
- Common::Rect icr = e->celRect;
- GuiResourceId picId = e->picture ? e->picture->getResourceId() : 0;
+ mulru(scaledPosition, Ratio(celObj._scaledWidth, _currentBuffer.screenWidth), Ratio(celObj._scaledHeight, _currentBuffer.screenHeight));
- con->debugPrintf("%d: %04x:%04x (%s), view %d, loop %d, cel %d, x %d, y %d, z %d, "
- "signal %d, scale signal %d, scaleX %d, scaleY %d, rect (%d, %d, %d, %d), "
- "pic %d, picX %d, picY %d, visible %d\n",
- e->givenOrderNr, PRINT_REG(e->object), curItemName.c_str(),
- e->viewId, e->loopNo, e->celNo, e->x, e->y, e->z,
- e->signal, e->scaleSignal, e->scaleX, e->scaleY,
- icr.left, icr.top, icr.right, icr.bottom,
- picId, e->picStartX, e->picStartY, e->visible);
+ if (screenItem._scale.signal != kScaleSignalNone && screenItem._scale.x && screenItem._scale.y) {
+ scaledPosition.x = scaledPosition.x * 128 / screenItem._scale.x;
+ scaledPosition.y = scaledPosition.y * 128 / screenItem._scale.y;
}
+
+ uint8 pixel = celObj.readPixel(scaledPosition.x, scaledPosition.y, mirrorX);
+ return pixel != celObj._transparentColor;
}
+
+ return true;
+}
+
+void GfxFrameout::kernelSetNowSeen(const reg_t screenItemObject) const {
+ const reg_t planeObject = readSelector(_segMan, screenItemObject, SELECTOR(plane));
+
+ Plane *plane = _planes.findByObject(planeObject);
+ if (plane == nullptr) {
+ error("kSetNowSeen: Plane %04x:%04x not found for screen item %04x:%04x", PRINT_REG(planeObject), PRINT_REG(screenItemObject));
+ }
+
+ ScreenItem *screenItem = plane->_screenItemList.findByObject(screenItemObject);
+ if (screenItem == nullptr) {
+ error("kSetNowSeen: Screen item %04x:%04x not found in plane %04x:%04x", PRINT_REG(screenItemObject), PRINT_REG(planeObject));
+ }
+
+ Common::Rect result = screenItem->getNowSeenRect(*plane);
+ writeSelectorValue(_segMan, screenItemObject, SELECTOR(nsLeft), result.left);
+ writeSelectorValue(_segMan, screenItemObject, SELECTOR(nsTop), result.top);
+ writeSelectorValue(_segMan, screenItemObject, SELECTOR(nsRight), result.right - 1);
+ writeSelectorValue(_segMan, screenItemObject, SELECTOR(nsBottom), result.bottom - 1);
+}
+
+void GfxFrameout::remapMarkRedraw() {
+ for (PlaneList::const_iterator it = _planes.begin(); it != _planes.end(); ++it) {
+ Plane *p = *it;
+ p->remapMarkRedraw();
+ }
+}
+
+#pragma mark -
+#pragma mark Debugging
+
+void GfxFrameout::printPlaneListInternal(Console *con, const PlaneList &planeList) const {
+ for (PlaneList::const_iterator it = planeList.begin(); it != planeList.end(); ++it) {
+ Plane *p = *it;
+ p->printDebugInfo(con);
+ }
+}
+
+void GfxFrameout::printPlaneList(Console *con) const {
+ printPlaneListInternal(con, _planes);
+}
+
+void GfxFrameout::printVisiblePlaneList(Console *con) const {
+ printPlaneListInternal(con, _visiblePlanes);
+}
+
+void GfxFrameout::printPlaneItemListInternal(Console *con, const ScreenItemList &screenItemList) const {
+ ScreenItemList::size_type i = 0;
+ for (ScreenItemList::const_iterator sit = screenItemList.begin(); sit != screenItemList.end(); sit++) {
+ ScreenItem *screenItem = *sit;
+ con->debugPrintf("%2d: ", i++);
+ screenItem->printDebugInfo(con);
+ }
+}
+
+void GfxFrameout::printPlaneItemList(Console *con, const reg_t planeObject) const {
+ Plane *p = _planes.findByObject(planeObject);
+
+ if (p == nullptr) {
+ con->debugPrintf("Plane does not exist");
+ return;
+ }
+
+ printPlaneItemListInternal(con, p->_screenItemList);
+}
+
+void GfxFrameout::printVisiblePlaneItemList(Console *con, const reg_t planeObject) const {
+ Plane *p = _visiblePlanes.findByObject(planeObject);
+
+ if (p == nullptr) {
+ con->debugPrintf("Plane does not exist");
+ return;
+ }
+
+ printPlaneItemListInternal(con, p->_screenItemList);
}
} // End of namespace Sci
diff --git a/engines/sci/graphics/frameout.h b/engines/sci/graphics/frameout.h
index d1a706e8de..8ed95a00de 100644
--- a/engines/sci/graphics/frameout.h
+++ b/engines/sci/graphics/frameout.h
@@ -23,86 +23,132 @@
#ifndef SCI_GRAPHICS_FRAMEOUT_H
#define SCI_GRAPHICS_FRAMEOUT_H
-namespace Sci {
+#include "sci/graphics/plane32.h"
+#include "sci/graphics/screen_item32.h"
-class GfxPicture;
+namespace Sci {
+// TODO: Don't do this this way
+int splitRects(Common::Rect r, const Common::Rect &other, Common::Rect(&outRects)[4]);
-struct PlaneLineEntry {
- reg_t hunkId;
- Common::Point startPoint;
- Common::Point endPoint;
- byte color;
- byte priority;
- byte control;
+// TODO: Verify display styles and adjust names appropriately for
+// types 1 through 12 & 15 (others are correct)
+// Names should be:
+// * VShutterIn, VShutterOut
+// * HShutterIn, HShutterOut
+// * WipeLeft, WipeRight, WipeDown, WipeUp
+// * PixelDissolve
+// * ShutDown and Kill? (and Plain and Fade?)
+enum ShowStyleType /* : uint8 */ {
+ kShowStyleNone = 0,
+ kShowStyleHShutterOut = 1,
+ kShowStyleHShutterIn = 2,
+ kShowStyleVShutterOut = 3,
+ kShowStyleVShutterIn = 4,
+ kShowStyleWipeLeft = 5,
+ kShowStyleWipeRight = 6,
+ kShowStyleWipeUp = 7,
+ kShowStyleWipeDown = 8,
+ kShowStyleIrisOut = 9,
+ kShowStyleIrisIn = 10,
+ kShowStyle11 = 11,
+ kShowStyle12 = 12,
+ kShowStyleFadeOut = 13,
+ kShowStyleFadeIn = 14,
+ // TODO: Only in SCI3
+ kShowStyleUnknown = 15
};
-typedef Common::List<PlaneLineEntry> PlaneLineList;
-
-struct PlaneEntry {
- reg_t object;
- int16 priority;
- int16 lastPriority;
- int16 planeOffsetX;
- int16 planeOffsetY;
- GuiResourceId pictureId;
- Common::Rect planeRect;
- Common::Rect planeClipRect;
- Common::Rect upscaledPlaneRect;
- Common::Rect upscaledPlaneClipRect;
- bool planePictureMirrored;
- byte planeBack;
- PlaneLineList lines;
-};
+/**
+ * Show styles represent transitions applied to draw planes.
+ * One show style per plane can be active at a time.
+ */
+struct ShowStyleEntry {
+ /**
+ * The ID of the plane this show style belongs to.
+ * In SCI2.1mid (at least SQ6), per-plane transitions
+ * were removed and a single plane ID is used.
+ */
+ reg_t plane;
-typedef Common::List<PlaneEntry> PlaneList;
-
-struct FrameoutEntry {
- uint16 givenOrderNr;
- reg_t object;
- GuiResourceId viewId;
- int16 loopNo;
- int16 celNo;
- int16 x, y, z;
- int16 priority;
- uint16 signal;
- uint16 scaleSignal;
- int16 scaleX;
- int16 scaleY;
- Common::Rect celRect;
- GfxPicture *picture;
- int16 picStartX;
- int16 picStartY;
- bool visible;
-};
+ /**
+ * The type of the transition.
+ */
+ ShowStyleType type;
-typedef Common::List<FrameoutEntry *> FrameoutList;
+ // TODO: This name is probably incorrect
+ bool fadeUp;
-struct PlanePictureEntry {
- reg_t object;
- int16 startX;
- int16 startY;
- GuiResourceId pictureId;
- GfxPicture *picture;
- FrameoutEntry *pictureCels; // temporary
-};
+ /**
+ * The number of steps for the show style.
+ */
+ int16 divisions;
-typedef Common::List<PlanePictureEntry> PlanePictureList;
+ // NOTE: This property exists from SCI2 through at least
+ // SCI2.1mid but is never used in the actual processing
+ // of the styles?
+ int unknownC;
-struct ScrollTextEntry {
- reg_t bitmapHandle;
- reg_t kWindow;
- uint16 x;
- uint16 y;
-};
+ /**
+ * The color used by transitions that draw CelObjColor
+ * screen items. -1 for transitions that do not draw
+ * screen items.
+ */
+ int16 color;
+
+ // TODO: Probably uint32
+ // TODO: This field probably should be used in order to
+ // provide time-accurate processing of show styles. In the
+ // actual SCI engine (at least 2–2.1mid) it appears that
+ // style transitions are drawn “as fast as possible”, one
+ // step per loop, even though this delay field exists
+ int delay;
+
+ // TODO: Probably bool, but never seems to be true?
+ int animate;
-typedef Common::Array<ScrollTextEntry> ScrollTextList;
+ /**
+ * The wall time at which the next step of the animation
+ * should execute.
+ */
+ uint32 nextTick;
-enum ViewScaleSignals32 {
- kScaleSignalDoScaling32 = 0x0001, // enables scaling when drawing that cel (involves scaleX and scaleY)
- kScaleSignalUnk1 = 0x0002, // unknown
- kScaleSignalDisableGlobalScaling32 = 0x0004
+ /**
+ * During playback of the show style, the current step
+ * (out of divisions).
+ */
+ int currentStep;
+
+ /**
+ * The next show style.
+ */
+ ShowStyleEntry *next;
+
+ /**
+ * Whether or not this style has finished running and
+ * is ready for disposal.
+ */
+ bool processed;
+
+ //
+ // Engine specific properties for SCI2.1mid through SCI3
+ //
+
+ /**
+ * The number of entries in the fadeColorRanges array.
+ */
+ uint8 fadeColorRangesCount;
+
+ /**
+ * A pointer to an dynamically sized array of palette
+ * indexes, in the order [ fromColor, toColor, ... ].
+ * Only colors within this range are transitioned.
+ */
+ uint16 *fadeColorRanges;
};
+typedef Common::Array<DrawList> ScreenItemListList;
+typedef Common::Array<RectList> EraseListList;
+
class GfxCache;
class GfxCoordAdjuster32;
class GfxPaint32;
@@ -114,69 +160,293 @@ class GfxScreen;
* Roughly equivalent to GraphicsMgr in the actual SCI engine.
*/
class GfxFrameout {
+private:
+ bool _isHiRes;
+ GfxCache *_cache;
+ GfxCoordAdjuster32 *_coordAdjuster;
+ GfxPalette32 *_palette;
+ ResourceManager *_resMan;
+ GfxScreen *_screen;
+ SegManager *_segMan;
+ GfxPaint32 *_paint32;
+
public:
GfxFrameout(SegManager *segMan, ResourceManager *resMan, GfxCoordAdjuster *coordAdjuster, GfxCache *cache, GfxScreen *screen, GfxPalette32 *palette, GfxPaint32 *paint32);
~GfxFrameout();
- void kernelAddPlane(reg_t object);
- void kernelUpdatePlane(reg_t object);
- void kernelDeletePlane(reg_t object);
- void applyGlobalScaling(FrameoutEntry *itemEntry, Common::Rect planeRect, int16 celHeight);
- void kernelAddScreenItem(reg_t object);
- void kernelUpdateScreenItem(reg_t object);
- void kernelDeleteScreenItem(reg_t object);
- void deletePlaneItems(reg_t planeObject);
- FrameoutEntry *findScreenItem(reg_t object);
- int16 kernelGetHighPlanePri();
- void kernelAddPicAt(reg_t planeObj, GuiResourceId pictureId, int16 pictureX, int16 pictureY);
- void kernelFrameout();
-
- void addPlanePicture(reg_t object, GuiResourceId pictureId, uint16 startX, uint16 startY = 0);
- void deletePlanePictures(reg_t object);
- reg_t addPlaneLine(reg_t object, Common::Point startPoint, Common::Point endPoint, byte color, byte priority, byte control);
- void updatePlaneLine(reg_t object, reg_t hunkId, Common::Point startPoint, Common::Point endPoint, byte color, byte priority, byte control);
- void deletePlaneLine(reg_t object, reg_t hunkId);
void clear();
+ void syncWithScripts(bool addElements); // this is what Game::restore does, only needed when our ScummVM dialogs are patched in
+ void run();
- // Scroll text functions
- void addScrollTextEntry(Common::String &text, reg_t kWindow, uint16 x, uint16 y, bool replace);
- void showCurrentScrollText();
- void initScrollText(uint16 maxItems) { _maxScrollTexts = maxItems; }
- void clearScrollTexts();
- void firstScrollText() { if (_scrollTexts.size() > 0) _curScrollText = 0; }
- void lastScrollText() { if (_scrollTexts.size() > 0) _curScrollText = _scrollTexts.size() - 1; }
- void prevScrollText() { if (_curScrollText > 0) _curScrollText--; }
- void nextScrollText() { if (_curScrollText + 1 < (uint16)_scrollTexts.size()) _curScrollText++; }
- void toggleScrollText(bool show) { _showScrollText = show; }
+#pragma mark -
+#pragma mark Screen items
+private:
+ void deleteScreenItem(ScreenItem *screenItem, const reg_t plane);
+ void remapMarkRedraw();
- void printPlaneList(Console *con);
- void printPlaneItemList(Console *con, reg_t planeObject);
+public:
+ void kernelAddScreenItem(const reg_t object);
+ void kernelUpdateScreenItem(const reg_t object);
+ void kernelDeleteScreenItem(const reg_t object);
+ void kernelSetNowSeen(const reg_t screenItemObject) const;
+#pragma mark -
+#pragma mark Planes
private:
- bool _isHiRes;
+ /**
+ * The list of planes (i.e. layers) that have been added
+ * to the screen.
+ *
+ * @note This field is on `GraphicsMgr.screen` in SCI
+ * engine.
+ */
+ PlaneList _planes;
- void showVideo();
- void createPlaneItemList(reg_t planeObject, FrameoutList &itemList);
- bool isPictureOutOfView(FrameoutEntry *itemEntry, Common::Rect planeRect, int16 planeOffsetX, int16 planeOffsetY);
- void drawPicture(FrameoutEntry *itemEntry, int16 planeOffsetX, int16 planeOffsetY, bool planePictureMirrored);
+ /**
+ * Updates an existing plane with properties from the
+ * given VM object.
+ */
+ void updatePlane(Plane &plane);
- SegManager *_segMan;
- ResourceManager *_resMan;
- GfxCoordAdjuster32 *_coordAdjuster;
- GfxCache *_cache;
- GfxPalette32 *_palette;
- GfxScreen *_screen;
- GfxPaint32 *_paint32;
+public:
+ /**
+ * Creates and adds a new plane to the plane list, or
+ * cancels deletion and updates an already-existing
+ * plane if a plane matching the given plane VM object
+ * already exists within the current plane list.
+ *
+ * @note This method is on Screen in SCI engine, but it
+ * is only ever called on `GraphicsMgr.screen`.
+ */
+ void addPlane(Plane &plane);
- FrameoutList _screenItems;
- PlaneList _planes;
- PlanePictureList _planePictures;
- ScrollTextList _scrollTexts;
- int16 _curScrollText;
- bool _showScrollText;
- uint16 _maxScrollTexts;
+ /**
+ * Deletes a plane within the current plane list.
+ *
+ * @note This method is on Screen in SCI engine, but it
+ * is only ever called on `GraphicsMgr.screen`.
+ */
+ void deletePlane(Plane &plane);
- void sortPlanes();
+ const PlaneList &getPlanes() const {
+ return _planes;
+ }
+ const PlaneList &getVisiblePlanes() const {
+ return _visiblePlanes;
+ }
+ void kernelAddPlane(const reg_t object);
+ void kernelUpdatePlane(const reg_t object);
+ void kernelDeletePlane(const reg_t object);
+ void kernelMovePlaneItems(const reg_t object, const int16 deltaX, const int16 deltaY, const bool scrollPics);
+ int16 kernelGetHighPlanePri();
+
+#pragma mark -
+#pragma mark Pics
+public:
+ void kernelAddPicAt(const reg_t planeObject, const GuiResourceId pictureId, const int16 pictureX, const int16 pictureY, const bool mirrorX);
+
+#pragma mark -
+
+ // TODO: Remap-related?
+ void kernelSetPalStyleRange(const uint8 fromColor, const uint8 toColor);
+
+#pragma mark -
+#pragma mark Transitions
+private:
+ int *_dissolveSequenceSeeds;
+ int16 *_defaultDivisions;
+ int16 *_defaultUnknownC;
+
+ /**
+ * TODO: Documentation
+ */
+ ShowStyleEntry *_showStyles;
+
+ inline ShowStyleEntry *findShowStyleForPlane(const reg_t planeObj) const;
+ inline ShowStyleEntry *deleteShowStyleInternal(ShowStyleEntry *const showStyle);
+ void processShowStyles();
+ bool processShowStyleNone(ShowStyleEntry *showStyle);
+ bool processShowStyleMorph(ShowStyleEntry *showStyle);
+ bool processShowStyleFade(const int direction, ShowStyleEntry *showStyle);
+
+public:
+ // NOTE: This signature is taken from SCI3 Phantasmagoria 2
+ // and is valid for all implementations of SCI32
+ void kernelSetShowStyle(const uint16 argc, const reg_t planeObj, const ShowStyleType type, const int16 seconds, const int16 direction, const int16 priority, const int16 animate, const int16 frameOutNow, reg_t pFadeArray, int16 divisions, const int16 blackScreen);
+
+#pragma mark -
+#pragma mark Rendering
+private:
+ /**
+ * TODO: Documentation
+ */
+ int8 _styleRanges[256];
+
+ /**
+ * The internal display pixel buffer. During frameOut,
+ * this buffer is drawn into according to the draw and
+ * erase rects calculated by `calcLists`, then drawn out
+ * to the hardware surface according to the `_showList`
+ * rects (which are also calculated by `calcLists`).
+ */
+ Buffer _currentBuffer;
+
+ /**
+ * TODO: Documentation
+ */
+ bool _remapOccurred;
+
+ /**
+ * Whether or not the data in the current buffer is what
+ * is visible to the user. During rendering updates,
+ * this flag is set to false.
+ */
+ bool _frameNowVisible;
+
+ /**
+ * TODO: Document
+ * TODO: Depending upon if the engine ever modifies this
+ * rect, it may be stupid to store it separately instead
+ * of just getting width/height from GfxScreen.
+ *
+ * @note This field is on `GraphicsMgr.screen` in SCI
+ * engine.
+ */
+ Common::Rect _screenRect;
+
+ /**
+ * A list of rectangles, in display coordinates, that
+ * represent portions of the internal screen buffer that
+ * should be drawn to the hardware display surface.
+ *
+ * @note This field is on `GraphicsMgr.screen` in SCI
+ * engine.
+ */
+ RectList _showList;
+
+ /**
+ * The amount of extra overdraw that is acceptable when
+ * merging two show list rectangles together into a
+ * single larger rectangle.
+ *
+ * @note This field is on `GraphicsMgr.screen` in SCI
+ * engine.
+ */
+ int _overdrawThreshold;
+
+ /**
+ * A list of planes that are currently drawn to the
+ * hardware display surface. Used to calculate
+ * differences in plane properties between the last
+ * frame and current frame.
+ *
+ * @note This field is on `GraphicsMgr.visibleScreen` in
+ * SCI engine.
+ */
+ PlaneList _visiblePlanes;
+
+ /**
+ * Calculates the location and dimensions of dirty rects
+ * over the entire screen for rendering the next frame.
+ * The draw and erase lists in `drawLists` and
+ * `eraseLists` each represent one plane on the screen.
+ */
+ void calcLists(ScreenItemListList &drawLists, EraseListList &eraseLists, const Common::Rect &calcRect);
+
+ /**
+ * Erases the areas in the given erase list from the
+ * visible screen buffer by filling them with the color
+ * from the corresponding plane. This is an optimisation
+ * for colored-type planes only; other plane types have
+ * to be redrawn from pixel data.
+ */
+ void drawEraseList(const RectList &eraseList, const Plane &plane);
+
+ /**
+ * Draws all screen items from the given draw list to
+ * the visible screen buffer.
+ */
+ void drawScreenItemList(const DrawList &screenItemList);
+
+ /**
+ * Adds a new rectangle to the list of regions to write
+ * out to the hardware. The provided rect may be merged
+ * into an existing rectangle to reduce the number of
+ * blit operations.
+ */
+ void mergeToShowList(const Common::Rect &drawRect, RectList &showList, const int overdrawThreshold);
+
+ /**
+ * TODO: Documentation
+ */
+ void palMorphFrameOut(const int8 *styleRanges, const ShowStyleEntry *showStyle);
+
+ /**
+ * Writes the internal frame buffer out to hardware and
+ * clears the show list.
+ */
+ void showBits();
+
+public:
+ /**
+ * Whether palMorphFrameOut should be used instead of
+ * frameOut for rendering. Used by kMorphOn to
+ * explicitly enable palMorphFrameOut for one frame.
+ */
+ bool _palMorphIsOn;
+
+ inline Buffer &getCurrentBuffer() {
+ return _currentBuffer;
+ }
+
+ void kernelFrameOut(const bool showBits);
+
+ /**
+ * Updates the internal screen buffer for the next
+ * frame. If `shouldShowBits` is true, also sends the
+ * buffer to hardware.
+ */
+ void frameOut(const bool shouldShowBits, const Common::Rect &rect = Common::Rect());
+
+ /**
+ * Modifies the raw pixel data for the next frame with
+ * new palette indexes based on matched style ranges.
+ */
+ void alterVmap(const Palette &palette1, const Palette &palette2, const int8 style, const int8 *const styleRanges);
+
+ // NOTE: This function is used within ScreenItem subsystem and assigned
+ // to various booleanish fields that seem to represent the state of the
+ // screen item (created, updated, deleted). In GK1/DOS, Phant1/m68k,
+ // SQ6/DOS, SQ6/Win, and Phant2/Win, this function simply returns 1. If
+ // you know of any game/environment where this function returns some
+ // value other than 1, or if you used to work at Sierra and can explain
+ // why this is a thing (and if anyone needs to care about it), please
+ // open a ticket!!
+ inline int getScreenCount() const {
+ return 1;
+ };
+
+#pragma mark -
+#pragma mark Mouse cursor
+private:
+ /**
+ * Determines whether or not the point given by
+ * `position` is inside of the given screen item.
+ */
+ bool isOnMe(const ScreenItem &screenItem, const Plane &plane, const Common::Point &position, const bool checkPixel) const;
+
+public:
+ reg_t kernelIsOnMe(const reg_t object, const Common::Point &position, const bool checkPixel) const;
+
+#pragma mark -
+#pragma mark Debugging
+public:
+ void printPlaneList(Console *con) const;
+ void printVisiblePlaneList(Console *con) const;
+ void printPlaneListInternal(Console *con, const PlaneList &planeList) const;
+ void printPlaneItemList(Console *con, const reg_t planeObject) const;
+ void printVisiblePlaneItemList(Console *con, const reg_t planeObject) const;
+ void printPlaneItemListInternal(Console *con, const ScreenItemList &screenItemList) const;
};
} // End of namespace Sci
diff --git a/engines/sci/graphics/helpers.h b/engines/sci/graphics/helpers.h
index e5b9f2aaed..19dddd74b8 100644
--- a/engines/sci/graphics/helpers.h
+++ b/engines/sci/graphics/helpers.h
@@ -26,6 +26,11 @@
#include "common/endian.h" // for READ_LE_UINT16
#include "common/rect.h"
#include "common/serializer.h"
+#ifdef ENABLE_SCI32
+#include "common/rational.h"
+#include "graphics/pixelformat.h"
+#include "graphics/surface.h"
+#endif
#include "sci/engine/vm_types.h"
namespace Sci {
@@ -45,6 +50,9 @@ typedef int16 TextAlignment;
#define PORTS_FIRSTWINDOWID 2
#define PORTS_FIRSTSCRIPTWINDOWID 3
+#ifdef ENABLE_SCI32
+#define PRINT_RECT(x) (x).left,(x).top,(x).right,(x).bottom
+#endif
struct Port {
uint16 id;
@@ -118,6 +126,102 @@ struct Window : public Port, public Common::Serializable {
}
};
+#ifdef ENABLE_SCI32
+/**
+ * Multiplies a rectangle by two ratios with default
+ * rounding. Modifies the rect directly.
+ */
+inline void mul(Common::Rect &rect, const Common::Rational &ratioX, const Common::Rational &ratioY) {
+ rect.left = (rect.left * ratioX).toInt();
+ rect.top = (rect.top * ratioY).toInt();
+ rect.right = (rect.right * ratioX).toInt();
+ rect.bottom = (rect.bottom * ratioY).toInt();
+}
+
+/**
+ * Multiplies a rectangle by two ratios with default
+ * rounding. Modifies the rect directly. Uses inclusive
+ * rectangle rounding.
+ */
+inline void mulinc(Common::Rect &rect, const Common::Rational &ratioX, const Common::Rational &ratioY) {
+ rect.left = (rect.left * ratioX).toInt();
+ rect.top = (rect.top * ratioY).toInt();
+ rect.right = ((rect.right - 1) * ratioX).toInt() + 1;
+ rect.bottom = ((rect.bottom - 1) * ratioY).toInt() + 1;
+}
+
+/**
+ * Multiplies a number by a rational number, rounding up to
+ * the nearest whole number.
+ */
+inline int mulru(const int value, const Common::Rational &ratio, const int extra = 0) {
+ int num = (value + extra) * ratio.getNumerator();
+ int result = num / ratio.getDenominator();
+ if (num > ratio.getDenominator() && num % ratio.getDenominator()) {
+ ++result;
+ }
+ return result - extra;
+}
+
+/**
+ * Multiplies a point by two rational numbers for X and Y,
+ * rounding up to the nearest whole number. Modifies the
+ * point directly.
+ */
+inline void mulru(Common::Point &point, const Common::Rational &ratioX, const Common::Rational &ratioY) {
+ point.x = mulru(point.x, ratioX);
+ point.y = mulru(point.y, ratioY);
+}
+
+/**
+ * Multiplies a point by two rational numbers for X and Y,
+ * rounding up to the nearest whole number. Modifies the
+ * rect directly.
+ */
+inline void mulru(Common::Rect &rect, const Common::Rational &ratioX, const Common::Rational &ratioY, const int extra) {
+ rect.left = mulru(rect.left, ratioX);
+ rect.top = mulru(rect.top, ratioY);
+ rect.right = mulru(rect.right - 1, ratioX, extra) + 1;
+ rect.bottom = mulru(rect.bottom - 1, ratioY, extra) + 1;
+}
+
+struct Buffer : public Graphics::Surface {
+ uint16 screenWidth;
+ uint16 screenHeight;
+ uint16 scriptWidth;
+ uint16 scriptHeight;
+
+ Buffer(const uint16 width, const uint16 height, uint8 *const pix) :
+ screenWidth(width),
+ screenHeight(height),
+ // TODO: These values are not correct for all games. Script
+ // dimensions were hard-coded per game in the original
+ // interpreter. Search all games for their internal script
+ // dimensions and set appropriately. (This code does not
+ // appear to exist at all in SCI3, which uses 640x480.)
+ scriptWidth(320),
+ scriptHeight(200) {
+ init(width, height, width, pix, Graphics::PixelFormat::createFormatCLUT8());
+ }
+
+ void clear(const uint8 value) {
+ memset(pixels, value, w * h);
+ }
+
+ inline uint8 *getAddress(const uint16 x, const uint16 y) {
+ return (uint8 *)getBasePtr(x, y);
+ }
+
+ inline uint8 *getAddressSimRes(const uint16 x, const uint16 y) {
+ return (uint8*)pixels + (y * w * screenHeight / scriptHeight) + (x * screenWidth / scriptWidth);
+ }
+
+ bool isNull() {
+ return pixels == nullptr;
+ }
+};
+#endif
+
struct Color {
byte used;
byte r, g, b;
@@ -146,7 +250,7 @@ struct Palette {
}
}
- return false;
+ return true;
}
inline bool operator!=(const Palette &other) const {
return !(*this == other);
diff --git a/engines/sci/graphics/lists32.h b/engines/sci/graphics/lists32.h
new file mode 100644
index 0000000000..4f74c77325
--- /dev/null
+++ b/engines/sci/graphics/lists32.h
@@ -0,0 +1,192 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 SCI_GRAPHICS_LISTS32_H
+#define SCI_GRAPHICS_LISTS32_H
+
+#include "common/array.h"
+
+namespace Sci {
+
+/**
+ * StablePointerArray holds pointers in a fixed-size array
+ * that maintains position of erased items until `pack` is
+ * called. It is used by DrawList, RectList, and
+ * ScreenItemList. StablePointerArray takes ownership of
+ * all pointers that are passed to it and deletes them when
+ * calling `erase` or when destroying the
+ * StablePointerArray.
+ */
+template<class T, uint N>
+class StablePointerArray {
+ uint _size;
+ T *_items[N];
+
+public:
+ typedef T **iterator;
+ typedef T *const *const_iterator;
+ typedef T *value_type;
+ typedef uint size_type;
+
+ StablePointerArray() : _size(0), _items() {}
+ StablePointerArray(const StablePointerArray &other) : _size(other._size) {
+ for (size_type i = 0; i < _size; ++i) {
+ if (other._items[i] == nullptr) {
+ _items[i] = nullptr;
+ } else {
+ _items[i] = new T(*other._items[i]);
+ }
+ }
+ }
+ ~StablePointerArray() {
+ for (size_type i = 0; i < _size; ++i) {
+ delete _items[i];
+ }
+ }
+
+ void operator=(const StablePointerArray &other) {
+ clear();
+ _size = other._size;
+ for (size_type i = 0; i < _size; ++i) {
+ if (other._items[i] == nullptr) {
+ _items[i] = nullptr;
+ } else {
+ _items[i] = new T(*other._items[i]);
+ }
+ }
+ }
+
+ T *const &operator[](size_type index) const {
+ assert(index < _size);
+ return _items[index];
+ }
+
+ T *&operator[](size_type index) {
+ assert(index < _size);
+ return _items[index];
+ }
+
+ /**
+ * Adds a new pointer to the array.
+ */
+ void add(T *item) {
+ assert(_size < N);
+ _items[_size++] = item;
+ }
+
+ iterator begin() {
+ return _items;
+ }
+
+ const_iterator begin() const {
+ return _items;
+ }
+
+ void clear() {
+ for (size_type i = 0; i < _size; ++i) {
+ delete _items[i];
+ _items[i] = nullptr;
+ }
+
+ _size = 0;
+ }
+
+ iterator end() {
+ return _items + _size;
+ }
+
+ const_iterator end() const {
+ return _items + _size;
+ }
+
+ /**
+ * Erases the object pointed to by the given iterator.
+ */
+ void erase(T *item) {
+ for (iterator it = begin(); it != end(); ++it) {
+ if (*it == item) {
+ delete *it;
+ *it = nullptr;
+ break;
+ }
+ }
+ }
+
+ /**
+ * Erases the object pointed to by the given iterator.
+ */
+ void erase(iterator &it) {
+ assert(it >= _items && it < _items + _size);
+ delete *it;
+ *it = nullptr;
+ }
+
+ /**
+ * Erases the object pointed to at the given index.
+ */
+ void erase_at(size_type index) {
+ assert(index < _size);
+
+ delete _items[index];
+ _items[index] = nullptr;
+ }
+
+ /**
+ * Removes freed pointers from the pointer list.
+ */
+ size_type pack() {
+ iterator freePtr = begin();
+ size_type newSize = 0;
+
+ for (iterator it = begin(), last = end(); it != last; ++it) {
+ if (*it != nullptr) {
+ *freePtr = *it;
+ ++freePtr;
+ ++newSize;
+ }
+ }
+
+ _size = newSize;
+ return newSize;
+ }
+
+ /**
+ * The number of populated slots in the array. The size
+ * of the array will only go down once `pack` is called.
+ */
+ size_type size() const {
+ return _size;
+ }
+};
+
+template<typename T>
+class FindByObject {
+ const reg_t &_object;
+public:
+ FindByObject(const reg_t &object) : _object(object) {}
+ bool operator()(const T entry) const {
+ return entry && entry->_object == _object;
+ }
+};
+
+} // End of namespace Sci
+#endif
diff --git a/engines/sci/graphics/palette.cpp b/engines/sci/graphics/palette.cpp
index 106924ecd3..1514ad838f 100644
--- a/engines/sci/graphics/palette.cpp
+++ b/engines/sci/graphics/palette.cpp
@@ -32,6 +32,7 @@
#include "sci/graphics/cache.h"
#include "sci/graphics/maciconbar.h"
#include "sci/graphics/palette.h"
+#include "sci/graphics/remap.h"
#include "sci/graphics/screen.h"
#include "sci/graphics/view.h"
@@ -103,9 +104,6 @@ GfxPalette::GfxPalette(ResourceManager *resMan, GfxScreen *screen)
default:
error("GfxPalette: Unknown view type");
}
-
- _remapOn = false;
- resetRemapping();
}
GfxPalette::~GfxPalette() {
@@ -310,7 +308,7 @@ void GfxPalette::set(Palette *newPalette, bool force, bool forceRealMerge) {
uint32 systime = _sysPalette.timestamp;
if (force || newPalette->timestamp != systime) {
- // SCI1.1+ doesnt do real merging anymore, but simply copying over the used colors from other palettes
+ // SCI1.1+ doesn't do real merging anymore, but simply copying over the used colors from other palettes
// There are some games with inbetween SCI1.1 interpreters, use real merging for them (e.g. laura bow 2 demo)
if ((forceRealMerge) || (_useMerging))
_sysPaletteChanged |= merge(newPalette, force, forceRealMerge);
@@ -336,79 +334,6 @@ void GfxPalette::set(Palette *newPalette, bool force, bool forceRealMerge) {
}
}
-byte GfxPalette::remapColor(byte remappedColor, byte screenColor) {
- assert(_remapOn);
- if (_remappingType[remappedColor] == kRemappingByRange)
- return _remappingByRange[screenColor];
- else if (_remappingType[remappedColor] == kRemappingByPercent)
- return _remappingByPercent[screenColor];
- else
- error("remapColor(): Color %d isn't remapped", remappedColor);
-
- return 0; // should never reach here
-}
-
-void GfxPalette::resetRemapping() {
- _remapOn = false;
- _remappingPercentToSet = 0;
-
- for (int i = 0; i < 256; i++) {
- _remappingType[i] = kRemappingNone;
- _remappingByPercent[i] = i;
- _remappingByRange[i] = i;
- }
-}
-
-void GfxPalette::setRemappingPercent(byte color, byte percent) {
- _remapOn = true;
-
- // We need to defer the setup of the remapping table every time the screen
- // palette is changed, so that kernelFindColor() can find the correct
- // colors. Set it once here, in case the palette stays the same and update
- // it on each palette change by copySysPaletteToScreen().
- _remappingPercentToSet = percent;
-
- for (int i = 0; i < 256; i++) {
- byte r = _sysPalette.colors[i].r * _remappingPercentToSet / 100;
- byte g = _sysPalette.colors[i].g * _remappingPercentToSet / 100;
- byte b = _sysPalette.colors[i].b * _remappingPercentToSet / 100;
- _remappingByPercent[i] = kernelFindColor(r, g, b);
- }
-
- _remappingType[color] = kRemappingByPercent;
-}
-
-void GfxPalette::setRemappingPercentGray(byte color, byte percent) {
- _remapOn = true;
-
- // We need to defer the setup of the remapping table every time the screen
- // palette is changed, so that kernelFindColor() can find the correct
- // colors. Set it once here, in case the palette stays the same and update
- // it on each palette change by copySysPaletteToScreen().
- _remappingPercentToSet = percent;
-
- // Note: This is not what the original does, but the results are the same visually
- for (int i = 0; i < 256; i++) {
- byte rComponent = (byte)(_sysPalette.colors[i].r * _remappingPercentToSet * 0.30 / 100);
- byte gComponent = (byte)(_sysPalette.colors[i].g * _remappingPercentToSet * 0.59 / 100);
- byte bComponent = (byte)(_sysPalette.colors[i].b * _remappingPercentToSet * 0.11 / 100);
- byte luminosity = rComponent + gComponent + bComponent;
- _remappingByPercent[i] = kernelFindColor(luminosity, luminosity, luminosity);
- }
-
- _remappingType[color] = kRemappingByPercent;
-}
-
-void GfxPalette::setRemappingRange(byte color, byte from, byte to, byte base) {
- _remapOn = true;
-
- for (int i = from; i <= to; i++) {
- _remappingByRange[i] = i + base;
- }
-
- _remappingType[color] = kRemappingByRange;
-}
-
bool GfxPalette::insert(Palette *newPalette, Palette *destPalette) {
bool paletteChanged = false;
@@ -589,15 +514,8 @@ void GfxPalette::copySysPaletteToScreen() {
}
}
- // Check if we need to reset remapping by percent with the new colors.
- if (_remappingPercentToSet) {
- for (int i = 0; i < 256; i++) {
- byte r = _sysPalette.colors[i].r * _remappingPercentToSet / 100;
- byte g = _sysPalette.colors[i].g * _remappingPercentToSet / 100;
- byte b = _sysPalette.colors[i].b * _remappingPercentToSet / 100;
- _remappingByPercent[i] = kernelFindColor(r, g, b);
- }
- }
+ if (g_sci->_gfxRemap16)
+ g_sci->_gfxRemap16->updateRemapping();
g_system->getPaletteManager()->setPalette(bpal, 0, 256);
}
diff --git a/engines/sci/graphics/palette.h b/engines/sci/graphics/palette.h
index 61a8d36cb9..7335dc59d0 100644
--- a/engines/sci/graphics/palette.h
+++ b/engines/sci/graphics/palette.h
@@ -35,12 +35,6 @@ class GfxScreen;
#define SCI_PALETTE_MATCH_PERFECT 0x8000
#define SCI_PALETTE_MATCH_COLORMASK 0xFF
-enum ColorRemappingType {
- kRemappingNone = 0,
- kRemappingByRange = 1,
- kRemappingByPercent = 2
-};
-
/**
* Palette class, handles palette operations like changing intensity, setting up the palette, merging different palettes
*/
@@ -64,15 +58,6 @@ public:
void getSys(Palette *pal);
uint16 getTotalColorCount() const { return _totalScreenColors; }
- void resetRemapping();
- void setRemappingPercent(byte color, byte percent);
- void setRemappingPercentGray(byte color, byte percent);
- void setRemappingRange(byte color, byte from, byte to, byte base);
- bool isRemapped(byte color) const {
- return _remapOn && (_remappingType[color] != kRemappingNone);
- }
- byte remapColor(byte remappedColor, byte screenColor);
-
void setOnScreen();
void copySysPaletteToScreen();
@@ -138,12 +123,6 @@ protected:
int _palVarySignal;
uint16 _totalScreenColors;
- bool _remapOn;
- ColorRemappingType _remappingType[256];
- byte _remappingByPercent[256];
- byte _remappingByRange[256];
- uint16 _remappingPercentToSet;
-
void loadMacIconBarPalette();
byte *_macClut;
};
diff --git a/engines/sci/graphics/palette32.cpp b/engines/sci/graphics/palette32.cpp
index e330b5620b..6844011675 100644
--- a/engines/sci/graphics/palette32.cpp
+++ b/engines/sci/graphics/palette32.cpp
@@ -28,29 +28,41 @@
#include "sci/event.h"
#include "sci/resource.h"
#include "sci/graphics/palette32.h"
+#include "sci/graphics/remap.h"
#include "sci/graphics/screen.h"
namespace Sci {
-
+
GfxPalette32::GfxPalette32(ResourceManager *resMan, GfxScreen *screen)
: GfxPalette(resMan, screen),
+ // Palette versioning
+ _version(1),
+ _versionUpdated(false),
+ _sourcePalette(_sysPalette),
+ _nextPalette(_sysPalette),
+ // Clut
_clutTable(nullptr),
- // Palette cycling
- _cyclers(), _cycleMap(),
// Palette varying
- _sourcePalette(_sysPalette), _nextPalette(_sysPalette),
- _varyTime(0), _varyDirection(0), _varyTargetPercent(0),
- _varyTargetPalette(nullptr), _varyStartPalette(nullptr),
- _varyFromColor(0), _varyToColor(255), _varyNumTimesPaused(0),
- _varyPercent(_varyTargetPercent), _varyLastTick(0),
- // Palette versioning
- _version(1), _versionUpdated(false) {
- memset(_fadeTable, 100, sizeof(_fadeTable));
-
+ _varyStartPalette(nullptr),
+ _varyTargetPalette(nullptr),
+ _varyFromColor(0),
+ _varyToColor(255),
+ _varyLastTick(0),
+ _varyTime(0),
+ _varyDirection(0),
+ _varyTargetPercent(0),
+ _varyNumTimesPaused(0),
+ // Palette cycling
+ _cyclers(),
+ _cycleMap() {
+ _varyPercent = _varyTargetPercent;
+ for (int i = 0, len = ARRAYSIZE(_fadeTable); i < len; ++i) {
+ _fadeTable[i] = 100;
+ }
// NOTE: In SCI engine, the palette manager constructor loads
// the default palette, but in ScummVM this initialisation
// is performed by SciEngine::run; see r49523 for details
- }
+}
GfxPalette32::~GfxPalette32() {
unloadClut();
@@ -59,13 +71,17 @@ GfxPalette32::~GfxPalette32() {
}
inline void mergePaletteInternal(Palette *const to, const Palette *const from) {
- for (int i = 0; i < ARRAYSIZE(to->colors); ++i) {
+ for (int i = 0, len = ARRAYSIZE(to->colors); i < len; ++i) {
if (from->colors[i].used) {
to->colors[i] = from->colors[i];
}
}
}
+const Palette *GfxPalette32::getNextPalette() const {
+ return &_nextPalette;
+}
+
void GfxPalette32::submit(Palette &palette) {
// TODO: The resource manager in SCI32 retains raw data of palettes from
// the ResourceManager (ResourceMgr) through SegManager (MemoryMgr), and
@@ -205,17 +221,22 @@ int16 GfxPalette32::matchColor(const byte r, const byte g, const byte b, const i
return bestIndex;
}
-void GfxPalette32::updateForFrame() {
+bool GfxPalette32::updateForFrame() {
applyAll();
_versionUpdated = false;
- // TODO: Implement remapping
- // g_sci->_gfxFrameout->remapAllTables(_nextPalette != _sysPalette);
+ return g_sci->_gfxRemap32->remapAllTables(_nextPalette != _sysPalette);
+}
+
+void GfxPalette32::updateFFrame() {
+ for (int i = 0; i < ARRAYSIZE(_nextPalette.colors); ++i) {
+ _nextPalette.colors[i] = _sourcePalette.colors[i];
+ }
+ _versionUpdated = false;
+ g_sci->_gfxRemap32->remapAllTables(_nextPalette != _sysPalette);
}
void GfxPalette32::updateHardware() {
if (_sysPalette == _nextPalette) {
- // TODO: This condition has never been encountered yet
- debug("Skipping hardware update because palettes are identical");
return;
}
@@ -246,9 +267,8 @@ void GfxPalette32::applyAll() {
applyFade();
}
-//
-// Clut
-//
+#pragma mark -
+#pragma mark Colour look-up
bool GfxPalette32::loadClut(uint16 clutId) {
// loadClut() will load a color lookup table from a clu file and set
@@ -300,9 +320,8 @@ void GfxPalette32::unloadClut() {
_clutTable = nullptr;
}
-//
-// Palette vary
-//
+#pragma mark -
+#pragma mark Varying
inline bool GfxPalette32::createPaletteFromResourceInternal(const GuiResourceId paletteId, Palette *const out) const {
Resource *palResource = _resMan->findResource(ResourceId(kResourceTypePalette, paletteId), false);
@@ -409,7 +428,7 @@ void GfxPalette32::setVaryPercent(const int16 percent, const int time, const int
}
int16 GfxPalette32::getVaryPercent() const {
- return abs(_varyPercent);
+ return ABS(_varyPercent);
}
void GfxPalette32::varyOff() {
@@ -542,9 +561,8 @@ void GfxPalette32::applyVary() {
}
}
-//
-// Palette cycling
-//
+#pragma mark -
+#pragma mark Cycling
inline void GfxPalette32::clearCycleMap(const uint16 fromColor, const uint16 numColorsToClear) {
bool *mapEntry = _cycleMap + fromColor;
@@ -677,14 +695,8 @@ void GfxPalette32::cycleAllOn() {
}
void GfxPalette32::cycleAllPause() {
- // TODO: The SCI SQ6 cycleAllPause function does not seem to perform
- // nullptr checking?? This would definitely cause null pointer
- // dereference in SCI code. I have not seen anything actually call
- // this function yet, so it is possible it is just unused and broken
- // in SCI SQ6. This assert exists so that if this function is called,
- // it is noticed and can be rechecked in the actual engine.
- // Obviously this code *does* do nullptr checks instead of crashing. :)
- assert(0);
+ // NOTE: The original engine did not check for null pointers in the
+ // palette cyclers pointer array.
for (int i = 0, len = ARRAYSIZE(_cyclers); i < len; ++i) {
PalCycler *cycler = _cyclers[i];
if (cycler != nullptr) {
@@ -768,11 +780,16 @@ void GfxPalette32::applyCycles() {
}
}
-//
-// Palette fading
-//
+#pragma mark -
+#pragma mark Fading
-void GfxPalette32::setFade(uint8 percent, uint8 fromColor, uint16 numColorsToFade) {
+// NOTE: There are some game scripts (like SQ6 Sierra logo and main menu) that call
+// setFade with numColorsToFade set to 256, but other parts of the engine like
+// processShowStyleNone use 255 instead of 256. It is not clear if this is because
+// the last palette entry is intentionally left unmodified, or if this is a bug
+// in the engine. It certainly seems confused because all other places that accept
+// color ranges typically receive values in the range of 0–255.
+void GfxPalette32::setFade(uint16 percent, uint8 fromColor, uint16 numColorsToFade) {
if (fromColor > numColorsToFade) {
return;
}
@@ -794,9 +811,10 @@ void GfxPalette32::applyFade() {
Color &color = _nextPalette.colors[i];
- color.r = (int16)color.r * _fadeTable[i] / 100;
- color.g = (int16)color.g * _fadeTable[i] / 100;
- color.b = (int16)color.b * _fadeTable[i] / 100;
+ color.r = MIN(255, (uint16)color.r * _fadeTable[i] / 100);
+ color.g = MIN(255, (uint16)color.g * _fadeTable[i] / 100);
+ color.b = MIN(255, (uint16)color.b * _fadeTable[i] / 100);
}
}
-}
+
+} // End of namespace Sci
diff --git a/engines/sci/graphics/palette32.h b/engines/sci/graphics/palette32.h
index 8744687e4c..a5450776dc 100644
--- a/engines/sci/graphics/palette32.h
+++ b/engines/sci/graphics/palette32.h
@@ -25,6 +25,7 @@
#include "sci/graphics/palette.h"
+namespace Sci {
enum PalCyclerDirection {
PalCycleBackward = 0,
PalCycleForward = 1
@@ -71,188 +72,213 @@ struct PalCycler {
uint16 numTimesPaused;
};
-namespace Sci {
- class GfxPalette32 : public GfxPalette {
- public:
- GfxPalette32(ResourceManager *resMan, GfxScreen *screen);
- ~GfxPalette32();
-
- protected:
- /**
- * The palette revision version. Increments once per game
- * loop that changes the source palette. TODO: Possibly
- * other areas also change _version, double-check once it
- * is all implemented.
- */
- uint32 _version;
-
- /**
- * Whether or not the palette manager version was updated
- * during this loop.
- */
- bool _versionUpdated;
-
- /**
- * The unmodified source palette loaded by kPalette. Additional
- * palette entries may be mixed into the source palette by
- * CelObj objects, which contain their own palettes.
- */
- Palette _sourcePalette;
-
- /**
- * The palette to be used when the hardware is next updated.
- * On update, _nextPalette is transferred to _sysPalette.
- */
- Palette _nextPalette;
-
- // SQ6 defines 10 cyclers
- PalCycler *_cyclers[10];
-
- /**
- * The cycle map is used to detect overlapping cyclers.
- * According to SCI engine code, when two cyclers overlap,
- * a fatal error has occurred and the engine will display
- * an error and then exit.
- */
- bool _cycleMap[256];
- inline void clearCycleMap(uint16 fromColor, uint16 numColorsToClear);
- inline void setCycleMap(uint16 fromColor, uint16 numColorsToClear);
- inline PalCycler *getCycler(uint16 fromColor);
-
- /**
- * The fade table records the expected intensity level of each pixel
- * in the palette that will be displayed on the next frame.
- */
- byte _fadeTable[256];
-
- /**
- * An optional lookup table used to remap RGB565 colors to a palette
- * index. Used by Phantasmagoria 2 in 8-bit color environments.
- */
- byte *_clutTable;
-
- /**
- * An optional palette used to describe the source colors used
- * in a palette vary operation. If this palette is not specified,
- * sourcePalette is used instead.
- */
- Palette *_varyStartPalette;
-
- /**
- * An optional palette used to describe the target colors used
- * in a palette vary operation.
- */
- Palette *_varyTargetPalette;
-
- /**
- * The minimum palette index that has been varied from the
- * source palette. 0–255
- */
- uint8 _varyFromColor;
-
- /**
- * The maximum palette index that is has been varied from the
- * source palette. 0-255
- */
- uint8 _varyToColor;
-
- /**
- * The tick at the last time the palette vary was updated.
- */
- uint32 _varyLastTick;
-
- /**
- * TODO: Document
- * The velocity of change in percent?
- */
- int _varyTime;
-
- /**
- * TODO: Better documentation
- * The direction of change, -1, 0, or 1.
- */
- int16 _varyDirection;
-
- /**
- * The amount, in percent, that the vary color is currently
- * blended into the source color.
- */
- int16 _varyPercent;
-
- /**
- * The target amount that a vary color will be blended into
- * the source color.
- */
- int16 _varyTargetPercent;
-
- /**
- * The number of time palette varying has been paused.
- */
- uint16 _varyNumTimesPaused;
-
- /**
- * Submits a palette to display. Entries marked as “used” in the
- * submitted palette are merged into the existing entries of
- * _sourcePalette.
- */
- void submit(Palette &palette);
-
- public:
- virtual void saveLoadWithSerializer(Common::Serializer &s) override;
-
- bool kernelSetFromResource(GuiResourceId resourceId, bool force) override;
- int16 kernelFindColor(uint16 r, uint16 g, uint16 b) override;
- void set(Palette *newPalette, bool force, bool forceRealMerge = false) override;
- int16 matchColor(const byte matchRed, const byte matchGreen, const byte matchBlue, const int defaultDifference, int &lastCalculatedDifference, const bool *const matchTable);
-
- void updateForFrame();
- void updateHardware();
- void applyAll();
-
- bool loadClut(uint16 clutId);
- byte matchClutColor(uint16 color);
- void unloadClut();
-
- void kernelPalVarySet(const GuiResourceId paletteId, const int16 percent, const int time, const int16 fromColor, const int16 toColor);
- void kernelPalVaryMergeTarget(const GuiResourceId paletteId);
- void kernelPalVarySetTarget(const GuiResourceId paletteId);
- void kernelPalVarySetStart(const GuiResourceId paletteId);
- void kernelPalVaryMergeStart(const GuiResourceId paletteId);
- virtual void kernelPalVaryPause(bool pause) override;
-
- void setVary(const Palette *const targetPalette, const int16 percent, const int time, const int16 fromColor, const int16 toColor);
- void setVaryPercent(const int16 percent, const int time, const int16 fromColor, const int16 fromColorAlternate);
- int16 getVaryPercent() const;
- void varyOff();
- void mergeTarget(const Palette *const palette);
- void varyPause();
- void varyOn();
- void setVaryTime(const int time);
- void setTarget(const Palette *const palette);
- void setStart(const Palette *const palette);
- void mergeStart(const Palette *const palette);
- private:
- bool createPaletteFromResourceInternal(const GuiResourceId paletteId, Palette *const out) const;
- Palette getPaletteFromResourceInternal(const GuiResourceId paletteId) const;
- void setVaryTimeInternal(const int16 percent, const int time);
- public:
- void applyVary();
-
- void setCycle(const uint8 fromColor, const uint8 toColor, const int16 direction, const int16 delay);
- void doCycle(const uint8 fromColor, const int16 speed);
- void cycleOn(const uint8 fromColor);
- void cyclePause(const uint8 fromColor);
- void cycleAllOn();
- void cycleAllPause();
- void cycleOff(const uint8 fromColor);
- void cycleAllOff();
- void applyAllCycles();
- void applyCycles();
-
- void setFade(const uint8 percent, const uint8 fromColor, const uint16 toColor);
- void fadeOff();
- void applyFade();
- };
-}
+class GfxPalette32 : public GfxPalette {
+public:
+ GfxPalette32(ResourceManager *resMan, GfxScreen *screen);
+ ~GfxPalette32();
+
+private:
+ // NOTE: currentPalette in SCI engine is called _sysPalette
+ // here.
+
+ /**
+ * The palette revision version. Increments once per game
+ * loop that changes the source palette. TODO: Possibly
+ * other areas also change _version, double-check once it
+ * is all implemented.
+ */
+ uint32 _version;
+
+ /**
+ * Whether or not the palette manager version was updated
+ * during this loop.
+ */
+ bool _versionUpdated;
+
+ /**
+ * The unmodified source palette loaded by kPalette. Additional
+ * palette entries may be mixed into the source palette by
+ * CelObj objects, which contain their own palettes.
+ */
+ Palette _sourcePalette;
+
+ /**
+ * The palette to be used when the hardware is next updated.
+ * On update, _nextPalette is transferred to _sysPalette.
+ */
+ Palette _nextPalette;
+
+ bool createPaletteFromResourceInternal(const GuiResourceId paletteId, Palette *const out) const;
+ Palette getPaletteFromResourceInternal(const GuiResourceId paletteId) const;
+
+public:
+ virtual void saveLoadWithSerializer(Common::Serializer &s) override;
+ const Palette *getNextPalette() const;
+
+ bool kernelSetFromResource(GuiResourceId resourceId, bool force) override;
+ int16 kernelFindColor(uint16 r, uint16 g, uint16 b) override;
+ void set(Palette *newPalette, bool force, bool forceRealMerge = false) override;
+ int16 matchColor(const byte matchRed, const byte matchGreen, const byte matchBlue, const int defaultDifference, int &lastCalculatedDifference, const bool *const matchTable);
+
+ /**
+ * Submits a palette to display. Entries marked as “used” in the
+ * submitted palette are merged into the existing entries of
+ * _sourcePalette.
+ */
+ void submit(Palette &palette);
+
+ bool updateForFrame();
+ void updateFFrame();
+ void updateHardware();
+ void applyAll();
+
+#pragma mark -
+#pragma mark color look-up
+private:
+ /**
+ * An optional lookup table used to remap RGB565 colors to a palette
+ * index. Used by Phantasmagoria 2 in 8-bit color environments.
+ */
+ byte *_clutTable;
+
+public:
+ bool loadClut(uint16 clutId);
+ byte matchClutColor(uint16 color);
+ void unloadClut();
+
+#pragma mark -
+#pragma mark Varying
+private:
+ /**
+ * An optional palette used to describe the source colors used
+ * in a palette vary operation. If this palette is not specified,
+ * sourcePalette is used instead.
+ */
+ Palette *_varyStartPalette;
+
+ /**
+ * An optional palette used to describe the target colors used
+ * in a palette vary operation.
+ */
+ Palette *_varyTargetPalette;
+
+ /**
+ * The minimum palette index that has been varied from the
+ * source palette. 0–255
+ */
+ uint8 _varyFromColor;
+
+ /**
+ * The maximum palette index that is has been varied from the
+ * source palette. 0-255
+ */
+ uint8 _varyToColor;
+
+ /**
+ * The tick at the last time the palette vary was updated.
+ */
+ uint32 _varyLastTick;
+
+ /**
+ * The amount of time to elapse, in ticks, between each cycle
+ * of a palette vary animation.
+ */
+ int _varyTime;
+
+ /**
+ * The direction of change: -1, 0, or 1.
+ */
+ int16 _varyDirection;
+
+ /**
+ * The amount, in percent, that the vary color is currently
+ * blended into the source color.
+ */
+ int16 _varyPercent;
+
+ /**
+ * The target amount that a vary color will be blended into
+ * the source color.
+ */
+ int16 _varyTargetPercent;
+
+ /**
+ * The number of time palette varying has been paused.
+ */
+ uint16 _varyNumTimesPaused;
+
+public:
+ void kernelPalVarySet(const GuiResourceId paletteId, const int16 percent, const int time, const int16 fromColor, const int16 toColor);
+ void kernelPalVaryMergeTarget(const GuiResourceId paletteId);
+ void kernelPalVarySetTarget(const GuiResourceId paletteId);
+ void kernelPalVarySetStart(const GuiResourceId paletteId);
+ void kernelPalVaryMergeStart(const GuiResourceId paletteId);
+ virtual void kernelPalVaryPause(bool pause) override;
+
+ void setVary(const Palette *const targetPalette, const int16 percent, const int time, const int16 fromColor, const int16 toColor);
+ void setVaryPercent(const int16 percent, const int time, const int16 fromColor, const int16 fromColorAlternate);
+ int16 getVaryPercent() const;
+ void varyOff();
+ void mergeTarget(const Palette *const palette);
+ void varyPause();
+ void varyOn();
+ void setVaryTime(const int time);
+ void setTarget(const Palette *const palette);
+ void setStart(const Palette *const palette);
+ void mergeStart(const Palette *const palette);
+ void setVaryTimeInternal(const int16 percent, const int time);
+ void applyVary();
+
+#pragma mark -
+#pragma mark Cycling
+private:
+ // SQ6 defines 10 cyclers
+ PalCycler *_cyclers[10];
+
+ /**
+ * The cycle map is used to detect overlapping cyclers.
+ * According to SCI engine code, when two cyclers overlap,
+ * a fatal error has occurred and the engine will display
+ * an error and then exit.
+ */
+ bool _cycleMap[256];
+ inline void clearCycleMap(uint16 fromColor, uint16 numColorsToClear);
+ inline void setCycleMap(uint16 fromColor, uint16 numColorsToClear);
+ inline PalCycler *getCycler(uint16 fromColor);
+
+public:
+ void setCycle(const uint8 fromColor, const uint8 toColor, const int16 direction, const int16 delay);
+ void doCycle(const uint8 fromColor, const int16 speed);
+ void cycleOn(const uint8 fromColor);
+ void cyclePause(const uint8 fromColor);
+ void cycleAllOn();
+ void cycleAllPause();
+ void cycleOff(const uint8 fromColor);
+ void cycleAllOff();
+ void applyAllCycles();
+ void applyCycles();
+ const bool *getCyclemap() { return _cycleMap; }
+
+#pragma mark -
+#pragma mark Fading
+private:
+ /**
+ * The fade table records the expected intensity level of each pixel
+ * in the palette that will be displayed on the next frame.
+ */
+ uint16 _fadeTable[256];
+
+public:
+ /**
+ * Sets the intensity level for a range of palette
+ * entries. An intensity of zero indicates total
+ * darkness. Intensity may be set to over 100 percent.
+ */
+ void setFade(const uint16 percent, const uint8 fromColor, const uint16 toColor);
+ void fadeOff();
+ void applyFade();
+};
+
+} // End of namespace Sci
#endif
diff --git a/engines/sci/graphics/picture.cpp b/engines/sci/graphics/picture.cpp
index d7ef84dc1e..2eab391afd 100644
--- a/engines/sci/graphics/picture.cpp
+++ b/engines/sci/graphics/picture.cpp
@@ -209,12 +209,12 @@ void GfxPicture::drawSci32Vga(int16 celNo, int16 drawX, int16 drawY, int16 pictu
}
// Header
- // [headerSize:WORD] [celCount:BYTE] [Unknown:BYTE] [Unknown:WORD] [paletteOffset:DWORD] [Unknown:DWORD]
+ // 0[headerSize:WORD] 2[celCount:BYTE] 3[Unknown:BYTE] 4[celHeaderSize:WORD] 6[paletteOffset:DWORD] 10[Unknown:WORD] 12[Unknown:WORD]
// cel-header follow afterwards, each is 42 bytes
// Cel-Header
- // [width:WORD] [height:WORD] [displaceX:WORD] [displaceY:WORD] [clearColor:BYTE] [compressed:BYTE]
+ // 0[width:WORD] 2[height:WORD] 4[displaceX:WORD] 6[displaceY:WORD] 8[clearColor:BYTE] 9[compressed:BYTE]
// offset 10-23 is unknown
- // [rleOffset:DWORD] [literalOffset:DWORD] [Unknown:WORD] [Unknown:WORD] [priority:WORD] [relativeXpos:WORD] [relativeYpos:WORD]
+ // 24[rleOffset:DWORD] 28[literalOffset:DWORD] 32[Unknown:WORD] 34[Unknown:WORD] 36[priority:WORD] 38[relativeXpos:WORD] 40[relativeYpos:WORD]
cel_headerPos += 42 * celNo;
diff --git a/engines/sci/graphics/picture.h b/engines/sci/graphics/picture.h
index 2404f99b41..942fa0f107 100644
--- a/engines/sci/graphics/picture.h
+++ b/engines/sci/graphics/picture.h
@@ -38,6 +38,9 @@ enum {
class GfxPorts;
class GfxScreen;
class GfxPalette;
+class GfxCoordAdjuster;
+class ResourceManager;
+class Resource;
/**
* Picture class, handles loading and displaying of picture resources
diff --git a/engines/sci/graphics/plane32.cpp b/engines/sci/graphics/plane32.cpp
new file mode 100644
index 0000000000..d05e4f79e1
--- /dev/null
+++ b/engines/sci/graphics/plane32.cpp
@@ -0,0 +1,907 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 "sci/console.h"
+#include "sci/engine/kernel.h"
+#include "sci/engine/selector.h"
+#include "sci/engine/state.h"
+#include "sci/graphics/frameout.h"
+#include "sci/graphics/lists32.h"
+#include "sci/graphics/plane32.h"
+#include "sci/graphics/remap.h"
+#include "sci/graphics/screen.h"
+#include "sci/graphics/screen_item32.h"
+
+namespace Sci {
+#pragma mark DrawList
+void DrawList::add(ScreenItem *screenItem, const Common::Rect &rect) {
+ DrawItem *drawItem = new DrawItem;
+ drawItem->screenItem = screenItem;
+ drawItem->rect = rect;
+ DrawListBase::add(drawItem);
+}
+
+#pragma mark -
+#pragma mark Plane
+uint16 Plane::_nextObjectId = 20000;
+
+Plane::Plane(const Common::Rect &gameRect, PlanePictureCodes pictureId) :
+_width(g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth),
+_height(g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight),
+_pictureId(pictureId),
+_mirrored(false),
+_back(0),
+_priorityChanged(0),
+_object(make_reg(0, _nextObjectId++)),
+_redrawAllCount(g_sci->_gfxFrameout->getScreenCount()),
+_created(g_sci->_gfxFrameout->getScreenCount()),
+_updated(0),
+_deleted(0),
+_moved(0),
+_gameRect(gameRect) {
+ convertGameRectToPlaneRect();
+ _priority = MAX(10000, g_sci->_gfxFrameout->getPlanes().getTopPlanePriority() + 1);
+ setType();
+ _screenRect = _planeRect;
+}
+
+Plane::Plane(reg_t object) :
+_width(g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth),
+_height(g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight),
+_priorityChanged(false),
+_object(object),
+_redrawAllCount(g_sci->_gfxFrameout->getScreenCount()),
+_created(g_sci->_gfxFrameout->getScreenCount()),
+_updated(0),
+_deleted(0),
+_moved(0) {
+ SegManager *segMan = g_sci->getEngineState()->_segMan;
+ _vanishingPoint.x = readSelectorValue(segMan, object, SELECTOR(vanishingX));
+ _vanishingPoint.y = readSelectorValue(segMan, object, SELECTOR(vanishingY));
+
+ _gameRect.left = readSelectorValue(segMan, object, SELECTOR(inLeft));
+ _gameRect.top = readSelectorValue(segMan, object, SELECTOR(inTop));
+ _gameRect.right = readSelectorValue(segMan, object, SELECTOR(inRight)) + 1;
+ _gameRect.bottom = readSelectorValue(segMan, object, SELECTOR(inBottom)) + 1;
+ convertGameRectToPlaneRect();
+
+ _back = readSelectorValue(segMan, object, SELECTOR(back));
+ _priority = readSelectorValue(segMan, object, SELECTOR(priority));
+ _pictureId = readSelectorValue(segMan, object, SELECTOR(picture));
+ setType();
+
+ _mirrored = readSelectorValue(segMan, object, SELECTOR(mirrored));
+ _screenRect = _planeRect;
+ changePic();
+}
+
+Plane::Plane(const Plane &other) :
+_pictureId(other._pictureId),
+_mirrored(other._mirrored),
+_field_34(other._field_34), _field_38(other._field_38),
+_field_3C(other._field_3C), _field_40(other._field_40),
+_back(other._back),
+_object(other._object),
+_priority(other._priority),
+_planeRect(other._planeRect),
+_gameRect(other._gameRect),
+_screenRect(other._screenRect),
+_screenItemList(other._screenItemList) {}
+
+void Plane::operator=(const Plane &other) {
+ _gameRect = other._gameRect;
+ _planeRect = other._planeRect;
+ _vanishingPoint = other._vanishingPoint;
+ _pictureId = other._pictureId;
+ _type = other._type;
+ _mirrored = other._mirrored;
+ _priority = other._priority;
+ _back = other._back;
+ _width = other._width;
+ _field_34 = other._field_34;
+ _height = other._height;
+ _screenRect = other._screenRect;
+ _field_3C = other._field_3C;
+ _priorityChanged = other._priorityChanged;
+}
+
+void Plane::init() {
+ _nextObjectId = 20000;
+}
+
+void Plane::convertGameRectToPlaneRect() {
+ const int16 screenWidth = g_sci->_gfxFrameout->getCurrentBuffer().screenWidth;
+ const int16 screenHeight = g_sci->_gfxFrameout->getCurrentBuffer().screenHeight;
+ const int16 scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth;
+ const int16 scriptHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight;
+
+ const Ratio ratioX = Ratio(screenWidth, scriptWidth);
+ const Ratio ratioY = Ratio(screenHeight, scriptHeight);
+
+ _planeRect = _gameRect;
+ mulru(_planeRect, ratioX, ratioY, 1);
+}
+
+void Plane::printDebugInfo(Console *con) const {
+ Common::String name;
+
+ if (_object.isNumber()) {
+ name = "-scummvm-";
+ } else {
+ name = g_sci->getEngineState()->_segMan->getObjectName(_object);
+ }
+
+ con->debugPrintf("%04x:%04x (%s): type %d, prio %d, pic %d, mirror %d, back %d\n",
+ PRINT_REG(_object),
+ name.c_str(),
+ _type,
+ _priority,
+ _pictureId,
+ _mirrored,
+ _back
+ );
+ con->debugPrintf(" game rect: (%d, %d, %d, %d), plane rect: (%d, %d, %d, %d)\n screen rect: (%d, %d, %d, %d)\n",
+ PRINT_RECT(_gameRect),
+ PRINT_RECT(_planeRect),
+ PRINT_RECT(_screenRect)
+ );
+ con->debugPrintf(" # screen items: %d\n", _screenItemList.size());
+}
+
+#pragma mark -
+#pragma mark Plane - Pic
+
+void Plane::addPicInternal(const GuiResourceId pictureId, const Common::Point *position, const bool mirrorX) {
+
+ uint16 celCount = 1000;
+ for (uint16 celNo = 0; celNo < celCount; ++celNo) {
+ CelObjPic *celObj = new CelObjPic(pictureId, celNo);
+ if (celCount == 1000) {
+ celCount = celObj->_celCount;
+ }
+
+ ScreenItem *screenItem = new ScreenItem(_object, celObj->_info);
+ screenItem->_pictureId = pictureId;
+ screenItem->_mirrorX = mirrorX;
+ screenItem->_priority = celObj->_priority;
+ screenItem->_fixPriority = true;
+ if (position != nullptr) {
+ screenItem->_position = *position + celObj->_relativePosition;
+ } else {
+ screenItem->_position = celObj->_relativePosition;
+ }
+ _screenItemList.add(screenItem);
+
+ delete screenItem->_celObj;
+ screenItem->_celObj = celObj;
+ }
+}
+
+void Plane::addPic(const GuiResourceId pictureId, const Common::Point &position, const bool mirrorX) {
+ deletePic(pictureId);
+ addPicInternal(pictureId, &position, mirrorX);
+ // NOTE: In SCI engine this method returned the pictureId of the
+ // plane, but this return value was never used
+}
+
+void Plane::changePic() {
+ _pictureChanged = false;
+
+ if (_type != kPlaneTypePicture) {
+ return;
+ }
+
+ addPicInternal(_pictureId, nullptr, _mirrored);
+}
+
+void Plane::deletePic(const GuiResourceId pictureId) {
+ for (ScreenItemList::iterator it = _screenItemList.begin(); it != _screenItemList.end(); ++it) {
+ ScreenItem *screenItem = *it;
+ if (screenItem->_pictureId == pictureId) {
+ screenItem->_created = 0;
+ screenItem->_updated = 0;
+ screenItem->_deleted = g_sci->_gfxFrameout->getScreenCount();
+ }
+ }
+}
+
+void Plane::deletePic(const GuiResourceId oldPictureId, const GuiResourceId newPictureId) {
+ deletePic(oldPictureId);
+ _pictureId = newPictureId;
+}
+
+void Plane::deleteAllPics() {
+ for (ScreenItemList::iterator it = _screenItemList.begin(); it != _screenItemList.end(); ++it) {
+ ScreenItem *screenItem = *it;
+ if (screenItem != nullptr && screenItem->_celInfo.type == kCelTypePic) {
+ if (screenItem->_created == 0) {
+ screenItem->_created = 0;
+ screenItem->_updated = 0;
+ screenItem->_deleted = g_sci->_gfxFrameout->getScreenCount();
+ } else {
+ _screenItemList.erase(it);
+ }
+ }
+ }
+
+ _screenItemList.pack();
+}
+
+#pragma mark -
+#pragma mark Plane - Rendering
+
+void Plane::breakDrawListByPlanes(DrawList &drawList, const PlaneList &planeList) const {
+ int index = planeList.findIndexByObject(_object);
+
+ for (DrawList::size_type i = 0; i < drawList.size(); ++i) {
+ for (PlaneList::size_type j = index + 1; j < planeList.size(); ++j) {
+ if (planeList[j]->_type != kPlaneTypeTransparent) {
+ Common::Rect ptr[4];
+ int count = splitRects(drawList[i]->rect, planeList[j]->_screenRect, ptr);
+ if (count != -1) {
+ for (int k = count - 1; k >= 0; --k) {
+ drawList.add(drawList[i]->screenItem, ptr[k]);
+ }
+
+ drawList.erase_at(i);
+ break;
+ }
+ }
+ }
+ }
+ drawList.pack();
+}
+
+void Plane::breakEraseListByPlanes(RectList &eraseList, const PlaneList &planeList) const {
+ int index = planeList.findIndexByObject(_object);
+
+ for (RectList::size_type i = 0; i < eraseList.size(); ++i) {
+ for (PlaneList::size_type j = index + 1; j < planeList.size(); ++j) {
+ if (planeList[j]->_type != kPlaneTypeTransparent) {
+ Common::Rect ptr[4];
+
+ int count = splitRects(*eraseList[i], planeList[j]->_screenRect, ptr);
+ if (count != -1) {
+ for (int k = count - 1; k >= 0; --k) {
+ eraseList.add(ptr[k]);
+ }
+
+ eraseList.erase_at(i);
+ break;
+ }
+ }
+ }
+ }
+ eraseList.pack();
+}
+
+void Plane::calcLists(Plane &visiblePlane, const PlaneList &planeList, DrawList &drawList, RectList &eraseList) {
+ ScreenItemList::size_type planeItemCount = _screenItemList.size();
+ ScreenItemList::size_type visiblePlaneItemCount = visiblePlane._screenItemList.size();
+
+ for (ScreenItemList::size_type i = 0; i < planeItemCount; ++i) {
+ ScreenItem *vitem = nullptr;
+ // NOTE: The original engine used an array without bounds checking
+ // so could just get the visible screen item directly; we need to
+ // verify that the index is actually within the valid range for
+ // the visible plane before accessing the item to avoid a range
+ // error.
+ if (i < visiblePlaneItemCount) {
+ vitem = visiblePlane._screenItemList[i];
+ }
+ ScreenItem *item = _screenItemList[i];
+
+ if (i < _screenItemList.size() && item != nullptr) {
+ if (item->_deleted) {
+ // add item's rect to erase list
+ if (
+ i < visiblePlane._screenItemList.size() &&
+ vitem != nullptr &&
+ !vitem->_screenRect.isEmpty()
+ ) {
+ if (g_sci->_gfxRemap32->getRemapCount()) {
+ mergeToRectList(vitem->_screenRect, eraseList);
+ } else {
+ eraseList.add(vitem->_screenRect);
+ }
+ }
+ } else if (item->_created) {
+ // add item to draw list
+ item->calcRects(*this);
+
+ if(!item->_screenRect.isEmpty()) {
+ if (g_sci->_gfxRemap32->getRemapCount()) {
+ drawList.add(item, item->_screenRect);
+ mergeToRectList(item->_screenRect, eraseList);
+ } else {
+ drawList.add(item, item->_screenRect);
+ }
+ }
+ } else if (item->_updated) {
+ // add old rect to erase list, new item to draw list
+ item->calcRects(*this);
+ if (g_sci->_gfxRemap32->getRemapCount()) {
+ // if item and vitem don't overlap, ...
+ if (item->_screenRect.isEmpty() ||
+ i >= visiblePlaneItemCount ||
+ vitem == nullptr ||
+ vitem->_screenRect.isEmpty() ||
+ !vitem->_screenRect.intersects(item->_screenRect)
+ ) {
+ // add item to draw list, and old rect to erase list
+ if (!item->_screenRect.isEmpty()) {
+ drawList.add(item, item->_screenRect);
+ mergeToRectList(item->_screenRect, eraseList);
+ }
+ if (
+ i < visiblePlaneItemCount &&
+ vitem != nullptr &&
+ !vitem->_screenRect.isEmpty()
+ ) {
+ mergeToRectList(vitem->_screenRect, eraseList);
+ }
+ } else {
+ // otherwise, add bounding box of old+new to erase list,
+ // and item to draw list
+
+ // TODO: This was changed from disasm, verify please!
+ Common::Rect extendedScreenRect = vitem->_screenRect;
+ extendedScreenRect.extend(item->_screenRect);
+
+ drawList.add(item, item->_screenRect);
+ mergeToRectList(extendedScreenRect, eraseList);
+ }
+ } else {
+ // if no active remaps, just add item to draw list and old rect
+ // to erase list
+ if (!item->_screenRect.isEmpty()) {
+ drawList.add(item, item->_screenRect);
+ }
+ if (
+ i < visiblePlaneItemCount &&
+ vitem != nullptr &&
+ !vitem->_screenRect.isEmpty()
+ ) {
+ eraseList.add(vitem->_screenRect);
+ }
+ }
+ }
+ }
+ }
+
+ // Remove parts of eraselist/drawlist that are covered by other planes
+ breakEraseListByPlanes(eraseList, planeList);
+ breakDrawListByPlanes(drawList, planeList);
+
+ // We store the current size of the drawlist, as we want to loop
+ // over the currently inserted entries later.
+ DrawList::size_type drawListSizePrimary = drawList.size();
+
+ if (/* TODO: dword_C6288 */ false) { // "high resolution pictures"????
+ _screenItemList.sort();
+ bool encounteredPic = false;
+ bool v81 = false;
+
+ for (RectList::size_type i = 0; i < eraseList.size(); ++i) {
+ const Common::Rect *rect = eraseList[i];
+
+ for (ScreenItemList::size_type j = 0; j < _screenItemList.size(); ++j) {
+ ScreenItem *item = _screenItemList[j];
+
+ if (j < _screenItemList.size() && item != nullptr) {
+ if (rect->intersects(item->_screenRect)) {
+ const Common::Rect intersection = rect->findIntersectingRect(item->_screenRect);
+ if (!item->_deleted) {
+ if (encounteredPic) {
+ if (item->_celInfo.type == kCelTypePic) {
+ if (v81 || item->_celInfo.celNo == 0) {
+ drawList.add(item, intersection);
+ }
+ } else {
+ if (!item->_updated && !item->_created) {
+ drawList.add(item, intersection);
+ }
+ v81 = true;
+ }
+ } else {
+ if (!item->_updated && !item->_created) {
+ drawList.add(item, intersection);
+ }
+ if (item->_celInfo.type == kCelTypePic) {
+ encounteredPic = true;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ _screenItemList.unsort();
+ } else {
+ // add all items overlapping the erase list to the draw list
+ for (RectList::size_type i = 0; i < eraseList.size(); ++i) {
+ for (ScreenItemList::size_type j = 0; j < _screenItemList.size(); ++j) {
+ ScreenItem *item = _screenItemList[j];
+ if (
+ item != nullptr &&
+ !item->_created && !item->_updated && !item->_deleted &&
+ eraseList[i]->intersects(item->_screenRect)
+ ) {
+ drawList.add(item, eraseList[i]->findIntersectingRect(item->_screenRect));
+ }
+ }
+ }
+ }
+
+ if (g_sci->_gfxRemap32->getRemapCount() == 0) { // no remaps active?
+ // Add all items that overlap with items in the drawlist and have higher
+ // priority.
+
+ // We only loop over "primary" items in the draw list, skipping
+ // those that were added because of the erase list in the previous loop,
+ // or those to be added in this loop.
+ for (DrawList::size_type i = 0; i < drawListSizePrimary; ++i) {
+ DrawItem *dli = drawList[i];
+
+ for (ScreenItemList::size_type j = 0; j < planeItemCount; ++j) {
+ ScreenItem *sli = _screenItemList[j];
+
+ if (
+ i < drawList.size() && dli != nullptr &&
+ j < _screenItemList.size() && sli != nullptr &&
+ !sli->_created && !sli->_updated && !sli->_deleted
+ ) {
+ ScreenItem *item = dli->screenItem;
+
+ if (
+ (sli->_priority > item->_priority || (sli->_priority == item->_priority && sli->_object > item->_object)) &&
+ dli->rect.intersects(sli->_screenRect)
+ ) {
+ drawList.add(sli, dli->rect.findIntersectingRect(sli->_screenRect));
+ }
+ }
+ }
+ }
+ }
+
+ decrementScreenItemArrayCounts(&visiblePlane, false);
+ _screenItemList.pack();
+ visiblePlane._screenItemList.pack();
+}
+
+void Plane::decrementScreenItemArrayCounts(Plane *visiblePlane, const bool forceUpdate) {
+ // The size of the screenItemList may change, so it is
+ // critical to re-check the size on each iteration
+ for (ScreenItemList::size_type i = 0; i < _screenItemList.size(); ++i) {
+ ScreenItem *item = _screenItemList[i];
+
+ if (item != nullptr) {
+ // update item in visiblePlane if item is updated
+ if (
+ item->_updated ||
+ (
+ forceUpdate &&
+ visiblePlane != nullptr &&
+ visiblePlane->_screenItemList.findByObject(item->_object) != nullptr
+ )
+ ) {
+ *visiblePlane->_screenItemList[i] = *_screenItemList[i];
+ }
+
+ if (item->_updated) {
+ item->_updated--;
+ }
+
+ // create new item in visiblePlane if item was added
+ if (item->_created) {
+ item->_created--;
+ if (visiblePlane != nullptr) {
+ visiblePlane->_screenItemList.add(new ScreenItem(*item));
+ }
+ }
+
+ // delete item from both planes if it was deleted
+ if (item->_deleted) {
+ item->_deleted--;
+ if (!item->_deleted) {
+ visiblePlane->_screenItemList.erase_at(i);
+ _screenItemList.erase_at(i);
+ }
+ }
+ }
+ }
+}
+
+void Plane::filterDownEraseRects(DrawList &drawList, RectList &eraseList, RectList &transparentEraseList) const {
+ if (_type == kPlaneTypeTransparent) {
+ for (RectList::size_type i = 0; i < transparentEraseList.size(); ++i) {
+ const Common::Rect *r = transparentEraseList[i];
+ for (ScreenItemList::size_type j = 0; j < _screenItemList.size(); ++j) {
+ ScreenItem *item = _screenItemList[j];
+ if (item != nullptr) {
+ if (r->intersects(item->_screenRect)) {
+ mergeToDrawList(j, *r, drawList);
+ }
+ }
+ }
+ }
+ } else {
+ for (RectList::size_type i = 0; i < transparentEraseList.size(); ++i) {
+ Common::Rect *r = transparentEraseList[i];
+ if (r->intersects(_screenRect)) {
+ r->clip(_screenRect);
+ mergeToRectList(*r, eraseList);
+
+ for (ScreenItemList::size_type j = 0; j < _screenItemList.size(); ++j) {
+ ScreenItem *item = _screenItemList[j];
+
+ if (item != nullptr) {
+ if (r->intersects(item->_screenRect)) {
+ mergeToDrawList(j, *r, drawList);
+ }
+ }
+ }
+
+ Common::Rect ptr[4];
+ const Common::Rect *r2 = transparentEraseList[i];
+ int count = splitRects(*r2, *r, ptr);
+ for (int k = count - 1; k >= 0; --k) {
+ transparentEraseList.add(ptr[k]);
+ }
+ transparentEraseList.erase_at(i);
+ }
+ }
+
+ transparentEraseList.pack();
+ }
+}
+
+void Plane::filterUpDrawRects(DrawList &transparentDrawList, const DrawList &drawList) const {
+ for (DrawList::size_type i = 0; i < drawList.size(); ++i) {
+ const Common::Rect &r = drawList[i]->rect;
+
+ for (ScreenItemList::size_type j = 0; j < _screenItemList.size(); ++j) {
+ ScreenItem *item = _screenItemList[j];
+ if (item != nullptr) {
+ if (r.intersects(item->_screenRect)) {
+ mergeToDrawList(j, r, transparentDrawList);
+ }
+ }
+ }
+ }
+}
+
+void Plane::filterUpEraseRects(DrawList &drawList, RectList &eraseList) const {
+ for (RectList::size_type i = 0; i < eraseList.size(); ++i) {
+ const Common::Rect &r = *eraseList[i];
+ for (ScreenItemList::size_type j = 0; j < _screenItemList.size(); ++j) {
+ ScreenItem *item = _screenItemList[j];
+
+ if (item != nullptr) {
+ if (r.intersects(item->_screenRect)) {
+ mergeToDrawList(j, r, drawList);
+ }
+ }
+ }
+ }
+}
+
+void Plane::mergeToDrawList(const ScreenItemList::size_type index, const Common::Rect &rect, DrawList &drawList) const {
+ RectList rects;
+
+ ScreenItem *item = _screenItemList[index];
+ Common::Rect r = item->_screenRect;
+ r.clip(rect);
+ rects.add(r);
+
+ for (RectList::size_type i = 0; i < rects.size(); ++i) {
+ r = *rects[i];
+
+ for (DrawList::size_type j = 0; j < drawList.size(); ++j) {
+ const DrawItem *drawitem = drawList[j];
+ if (item->_object == drawitem->screenItem->_object) {
+ if (drawitem->rect.contains(r)) {
+ rects.erase_at(i);
+ break;
+ }
+
+ Common::Rect outRects[4];
+ const int count = splitRects(r, drawitem->rect, outRects);
+ if (count != -1) {
+ for (int k = count - 1; k >= 0; --k) {
+ rects.add(outRects[k]);
+ }
+
+ rects.erase_at(i);
+
+ // proceed to the next rect
+ r = *rects[++i];
+ }
+ }
+ }
+ }
+
+ rects.pack();
+
+ for (RectList::size_type i = 0; i < rects.size(); ++i) {
+ drawList.add(item, *rects[i]);
+ }
+}
+
+void Plane::mergeToRectList(const Common::Rect &rect, RectList &rectList) const {
+ RectList temp;
+ temp.add(rect);
+
+ for (RectList::size_type i = 0; i < temp.size(); ++i) {
+ Common::Rect r = *temp[i];
+
+ for (RectList::size_type j = 0; j < rectList.size(); ++j) {
+ const Common::Rect *innerRect = rectList[j];
+ if (innerRect->contains(r)) {
+ temp.erase_at(i);
+ break;
+ }
+
+ Common::Rect out[4];
+ const int count = splitRects(r, *innerRect, out);
+ if (count != -1) {
+ for (int k = count - 1; k >= 0; --k) {
+ temp.add(out[k]);
+ }
+
+ temp.erase_at(i);
+
+ // proceed to the next rect
+ r = *temp[++i];
+ }
+ }
+ }
+
+ temp.pack();
+
+ for (RectList::size_type i = 0; i < temp.size(); ++i) {
+ rectList.add(*temp[i]);
+ }
+}
+
+void Plane::redrawAll(Plane *visiblePlane, const PlaneList &planeList, DrawList &drawList, RectList &eraseList) {
+ for (ScreenItemList::const_iterator screenItemPtr = _screenItemList.begin(); screenItemPtr != _screenItemList.end(); ++screenItemPtr) {
+ if (*screenItemPtr != nullptr) {
+ ScreenItem &screenItem = **screenItemPtr;
+ if (!screenItem._deleted) {
+ screenItem.calcRects(*this);
+ if (!screenItem._screenRect.isEmpty()) {
+ drawList.add(&screenItem, screenItem._screenRect);
+ }
+ }
+ }
+ }
+
+ eraseList.clear();
+
+ if (!_screenRect.isEmpty() && _type != kPlaneTypePicture && _type != kPlaneTypeOpaque) {
+ eraseList.add(_screenRect);
+ }
+ breakEraseListByPlanes(eraseList, planeList);
+ breakDrawListByPlanes(drawList, planeList);
+ --_redrawAllCount;
+ decrementScreenItemArrayCounts(visiblePlane, true);
+ _screenItemList.pack();
+ if (visiblePlane != nullptr) {
+ visiblePlane->_screenItemList.pack();
+ }
+}
+
+void Plane::setType() {
+ if (_pictureId == kPlanePicOpaque) {
+ _type = kPlaneTypeOpaque;
+ } else if (_pictureId == kPlanePicTransparent) {
+ _type = kPlaneTypeTransparent;
+ } else if (_pictureId == kPlanePicColored) {
+ _type = kPlaneTypeColored;
+ } else {
+ _type = kPlaneTypePicture;
+ }
+}
+
+void Plane::sync(const Plane *other, const Common::Rect &screenRect) {
+ if (other == nullptr) {
+ if (_pictureChanged) {
+ deleteAllPics();
+ setType();
+ changePic();
+ _redrawAllCount = g_sci->_gfxFrameout->getScreenCount();
+ } else {
+ setType();
+ }
+ } else {
+ if (
+ _planeRect.top != other->_planeRect.top ||
+ _planeRect.left != other->_planeRect.left ||
+ _planeRect.right > other->_planeRect.right ||
+ _planeRect.bottom > other->_planeRect.bottom
+ ) {
+ _redrawAllCount = g_sci->_gfxFrameout->getScreenCount();
+ _updated = g_sci->_gfxFrameout->getScreenCount();
+ } else if (_planeRect != other->_planeRect) {
+ _updated = g_sci->_gfxFrameout->getScreenCount();
+ }
+
+ if (_priority != other->_priority) {
+ _priorityChanged = g_sci->_gfxFrameout->getScreenCount();
+ }
+
+ if (_pictureId != other->_pictureId || _mirrored != other->_mirrored || _pictureChanged) {
+ deleteAllPics();
+ setType();
+ changePic();
+ _redrawAllCount = g_sci->_gfxFrameout->getScreenCount();
+ }
+
+ if (_back != other->_back) {
+ _redrawAllCount = g_sci->_gfxFrameout->getScreenCount();
+ }
+ }
+
+ _deleted = 0;
+ if (_created == 0) {
+ _moved = g_sci->_gfxFrameout->getScreenCount();
+ }
+
+ convertGameRectToPlaneRect();
+ _width = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth;
+ _height = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight;
+ _screenRect = _planeRect;
+ // NOTE: screenRect originally was retrieved through globals
+ // instead of being passed into the function
+ clipScreenRect(screenRect);
+}
+
+void Plane::update(const reg_t object) {
+ SegManager *segMan = g_sci->getEngineState()->_segMan;
+ _vanishingPoint.x = readSelectorValue(segMan, object, SELECTOR(vanishingX));
+ _vanishingPoint.y = readSelectorValue(segMan, object, SELECTOR(vanishingY));
+ _gameRect.left = readSelectorValue(segMan, object, SELECTOR(inLeft));
+ _gameRect.top = readSelectorValue(segMan, object, SELECTOR(inTop));
+ _gameRect.right = readSelectorValue(segMan, object, SELECTOR(inRight)) + 1;
+ _gameRect.bottom = readSelectorValue(segMan, object, SELECTOR(inBottom)) + 1;
+ convertGameRectToPlaneRect();
+
+ _priority = readSelectorValue(segMan, object, SELECTOR(priority));
+ GuiResourceId pictureId = readSelectorValue(segMan, object, SELECTOR(picture));
+ if (_pictureId != pictureId) {
+ _pictureId = pictureId;
+ _pictureChanged = true;
+ }
+
+ _mirrored = readSelectorValue(segMan, object, SELECTOR(mirrored));
+ _back = readSelectorValue(segMan, object, SELECTOR(back));
+}
+
+void Plane::scrollScreenItems(const int16 deltaX, const int16 deltaY, const bool scrollPics) {
+ _redrawAllCount = g_sci->_gfxFrameout->getScreenCount();
+
+ for (ScreenItemList::iterator it = _screenItemList.begin(); it != _screenItemList.end(); ++it) {
+ if (*it != nullptr) {
+ ScreenItem &screenItem = **it;
+ if (!screenItem._deleted && (screenItem._celInfo.type != kCelTypePic || scrollPics)) {
+ screenItem._position.x += deltaX;
+ screenItem._position.y += deltaY;
+ }
+ }
+ }
+}
+
+void Plane::remapMarkRedraw() {
+ for (ScreenItemList::const_iterator screenItemPtr = _screenItemList.begin(); screenItemPtr != _screenItemList.end(); ++screenItemPtr) {
+ if (*screenItemPtr != nullptr) {
+ ScreenItem &screenItem = **screenItemPtr;
+ if (screenItem.getCelObj()._remap && !screenItem._deleted && !screenItem._created) {
+ screenItem._updated = g_sci->_gfxFrameout->getScreenCount();
+ }
+ }
+ }
+}
+
+#pragma mark -
+#pragma mark PlaneList
+void PlaneList::add(Plane *plane) {
+ for (iterator it = begin(); it != end(); ++it) {
+ if ((*it)->_priority > plane->_priority) {
+ insert(it, plane);
+ return;
+ }
+ }
+
+ push_back(plane);
+}
+
+void PlaneList::clear() {
+ for (iterator it = begin(); it != end(); ++it) {
+ delete *it;
+ }
+
+ PlaneListBase::clear();
+}
+
+void PlaneList::erase(Plane *plane) {
+ for (iterator it = begin(); it != end(); ++it) {
+ if (*it == plane) {
+ erase(it);
+ break;
+ }
+ }
+}
+
+PlaneList::iterator PlaneList::erase(iterator it) {
+ delete *it;
+ return PlaneListBase::erase(it);
+}
+
+int PlaneList::findIndexByObject(const reg_t object) const {
+ for (size_type i = 0; i < size(); ++i) {
+ if ((*this)[i] != nullptr && (*this)[i]->_object == object) {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+Plane *PlaneList::findByObject(const reg_t object) const {
+ const_iterator planeIt = Common::find_if(begin(), end(), FindByObject<Plane *>(object));
+
+ if (planeIt == end()) {
+ return nullptr;
+ }
+
+ return *planeIt;
+}
+
+int16 PlaneList::getTopPlanePriority() const {
+ if (size() > 0) {
+ return (*this)[size() - 1]->_priority;
+ }
+
+ return 0;
+}
+
+int16 PlaneList::getTopSciPlanePriority() const {
+ int16 priority = 0;
+
+ for (const_iterator it = begin(); it != end(); ++it) {
+ if ((*it)->_priority >= 10000) {
+ break;
+ }
+
+ priority = (*it)->_priority;
+ }
+
+ return priority;
+}
+
+void PlaneList::remove_at(size_type index) {
+ delete PlaneListBase::remove_at(index);
+}
+
+} // End of namespace Sci
diff --git a/engines/sci/graphics/plane32.h b/engines/sci/graphics/plane32.h
new file mode 100644
index 0000000000..770a6fa445
--- /dev/null
+++ b/engines/sci/graphics/plane32.h
@@ -0,0 +1,485 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 SCI_GRAPHICS_PLANE32_H
+#define SCI_GRAPHICS_PLANE32_H
+
+#include "common/array.h"
+#include "common/rect.h"
+#include "sci/engine/vm_types.h"
+#include "sci/graphics/helpers.h"
+#include "sci/graphics/lists32.h"
+#include "sci/graphics/screen_item32.h"
+
+namespace Sci {
+enum PlaneType {
+ kPlaneTypeColored = 0,
+ kPlaneTypePicture = 1,
+ kPlaneTypeTransparent = 2,
+ kPlaneTypeOpaque = 3
+};
+
+enum PlanePictureCodes {
+ // NOTE: Any value at or below 65532 means the plane
+ // is a kPlaneTypePicture.
+ kPlanePic = 65532,
+ kPlanePicOpaque = 65533,
+ kPlanePicTransparent = 65534,
+ kPlanePicColored = 65535
+};
+
+#pragma mark -
+#pragma mark RectList
+
+typedef StablePointerArray<Common::Rect, 200> RectListBase;
+class RectList : public RectListBase {
+public:
+ void add(const Common::Rect &rect) {
+ RectListBase::add(new Common::Rect(rect));
+ }
+};
+
+#pragma mark -
+#pragma mark DrawList
+
+struct DrawItem {
+ ScreenItem *screenItem;
+ Common::Rect rect;
+
+ inline bool operator<(const DrawItem &other) const {
+ return *screenItem < *other.screenItem;
+ }
+};
+
+typedef StablePointerArray<DrawItem, 250> DrawListBase;
+class DrawList : public DrawListBase {
+private:
+ inline static bool sortHelper(const DrawItem *a, const DrawItem *b) {
+ return *a < *b;
+ }
+public:
+ void add(ScreenItem *screenItem, const Common::Rect &rect);
+ inline void sort() {
+ pack();
+ Common::sort(begin(), end(), sortHelper);
+ }
+};
+
+class PlaneList;
+
+#pragma mark -
+#pragma mark Plane
+
+/**
+ * A plane is a grouped layer of screen items.
+ */
+class Plane {
+private:
+ /**
+ * A serial used for planes that are generated inside
+ * the graphics engine, rather than the interpreter.
+ */
+ static uint16 _nextObjectId;
+
+ /**
+ * The dimensions of the plane, in game script
+ * coordinates.
+ * TODO: These are never used and are always
+ * scriptWidth x scriptHeight in SCI engine? The actual
+ * dimensions of the plane are always in
+ * gameRect/planeRect.
+ */
+ int16 _width, _height;
+
+ /**
+ * For planes that are used to render picture data, the
+ * resource ID of the picture to be displayed. This
+ * value may also be one of the special
+ * PlanePictureCodes, in which case the plane becomes a
+ * non-picture plane.
+ */
+ GuiResourceId _pictureId;
+
+ /**
+ * Whether or not the contents of picture planes should
+ * be drawn horizontally mirrored. Only applies to
+ * planes of type kPlaneTypePicture.
+ */
+ bool _mirrored;
+
+ /**
+ * Whether the picture ID for this plane has changed.
+ * This flag is set when the plane is created or updated
+ * from a VM object, and is cleared when the plane is
+ * synchronised to another plane (which calls
+ * changePic).
+ */
+ bool _pictureChanged;
+
+ // TODO: Are these ever actually used?
+ int _field_34, _field_38; // probably a point or ratio
+ int _field_3C, _field_40; // probably a point or ratio
+
+ /**
+ * Converts the dimensions of the game rect used by
+ * scripts to the dimensions of the plane rect used to
+ * render content to the screen. Coordinates with
+ * remainders are rounded up to the next whole pixel.
+ */
+ void convertGameRectToPlaneRect();
+
+ /**
+ * Sets the type of the plane according to its assigned
+ * picture resource ID.
+ */
+ void setType();
+
+public:
+ /**
+ * The color to use when erasing the plane. Only
+ * applies to planes of type kPlaneTypeColored.
+ */
+ byte _back;
+
+ /**
+ * Whether the priority of this plane has changed.
+ * This flag is set when the plane is updated from
+ * another plane and cleared when draw list calculation
+ * occurs.
+ */
+ int _priorityChanged;
+
+ /**
+ * A handle to the VM object corresponding to this
+ * plane. Some planes are generated purely within the
+ * graphics engine and have a numeric object value.
+ */
+ reg_t _object;
+
+ /**
+ * The rendering priority of the plane. Higher
+ * priorities are drawn above lower priorities.
+ */
+ int16 _priority;
+
+ /**
+ * Whether or not all screen items in this plane should
+ * be redrawn on the next frameout, instead of just
+ * the screen items marked as updated. This is set when
+ * visual changes to the plane itself are made that
+ * affect the rendering of the entire plane, and cleared
+ * once those changes are rendered by `redrawAll`.
+ */
+ int _redrawAllCount;
+
+ PlaneType _type;
+
+ /**
+ * Flags indicating the state of the plane.
+ * - `created` is set when the plane is first created,
+ * either from a VM object or from within the engine
+ * itself
+ * - `updated` is set when the plane is updated from
+ * another plane and the two planes' `planeRect`s do
+ * not match
+ * - `deleted` is set when the plane is deleted by a
+ * kernel call
+ * - `moved` is set when the plane is synchronised from
+ * another plane and is not already in the "created"
+ * state
+ */
+ int _created, _updated, _deleted, _moved;
+
+ /**
+ * The vanishing point for the plane. Used when
+ * calculating the correct scaling of the plane's screen
+ * items according to their position.
+ */
+ Common::Point _vanishingPoint;
+
+ /**
+ * The position & dimensions of the plane in screen
+ * coordinates. This rect is not clipped to the screen,
+ * so may include coordinates that are offscreen.
+ */
+ Common::Rect _planeRect;
+
+ /**
+ * The position & dimensions of the plane in game script
+ * coordinates.
+ */
+ Common::Rect _gameRect;
+
+ /**
+ * The position & dimensions of the plane in screen
+ * coordinates. This rect is clipped to the screen.
+ */
+ Common::Rect _screenRect;
+
+ /**
+ * The list of screen items grouped within this plane.
+ */
+ ScreenItemList _screenItemList;
+
+public:
+ /**
+ * Initialises static Plane members.
+ */
+ static void init();
+
+ // NOTE: This constructor signature originally did not accept a
+ // picture ID, but some calls to construct planes with this signature
+ // immediately set the picture ID and then called setType again, so
+ // it made more sense to just make the picture ID a parameter instead.
+ Plane(const Common::Rect &gameRect, PlanePictureCodes pictureId = kPlanePicColored);
+
+ Plane(const reg_t object);
+
+ Plane(const Plane &other);
+
+ void operator=(const Plane &other);
+
+ inline bool operator<(const Plane &other) const {
+ if (_priority < other._priority) {
+ return true;
+ }
+
+ if (_priority == other._priority) {
+ return _object < other._object;
+ }
+
+ return false;
+ }
+
+ /**
+ * Clips the screen rect of this plane to fit within the
+ * given screen rect.
+ */
+ inline void clipScreenRect(const Common::Rect &screenRect) {
+ if (_screenRect.intersects(screenRect)) {
+ _screenRect.clip(screenRect);
+ } else {
+ _screenRect.left = 0;
+ _screenRect.top = 0;
+ _screenRect.right = 0;
+ _screenRect.bottom = 0;
+ }
+ }
+
+ void printDebugInfo(Console *con) const;
+
+ /**
+ * Compares the properties of the current plane against
+ * the properties of the `other` plane (which is the
+ * corresponding plane from the visible plane list) to
+ * discover which properties have been changed on this
+ * plane by a call to `update(reg_t)`.
+ *
+ * @note This method was originally called UpdatePlane
+ * in SCI engine.
+ */
+ void sync(const Plane *other, const Common::Rect &screenRect);
+
+ /**
+ * Updates the plane to match the state of the plane
+ * object from the virtual machine.
+ *
+ * @note This method was originally called UpdatePlane
+ * in SCI engine.
+ */
+ void update(const reg_t object);
+
+ /**
+ * Modifies the position of all non-pic screen items
+ * by the given delta. If `scrollPics` is true, pic
+ * items are also repositioned.
+ */
+ void scrollScreenItems(const int16 deltaX, const int16 deltaY, const bool scrollPics);
+
+#pragma mark -
+#pragma mark Plane - Pic
+private:
+ /**
+ * Adds all cels from the specified picture resource to
+ * the plane as screen items. If a position is provided,
+ * the screen items will be given that position;
+ * otherwise, the default relative positions for each
+ * cel will be taken from the picture resource data.
+ */
+ inline void addPicInternal(const GuiResourceId pictureId, const Common::Point *position, const bool mirrorX);
+
+ /**
+ * Marks all screen items to be deleted that are within
+ * this plane and match the given picture ID.
+ */
+ void deletePic(const GuiResourceId pictureId);
+
+ /**
+ * Marks all screen items to be deleted that are within
+ * this plane and match the given picture ID, then sets
+ * the picture ID of the plane to the new picture ID
+ * without adding any screen items.
+ */
+ void deletePic(const GuiResourceId oldPictureId, const GuiResourceId newPictureId);
+
+ /**
+ * Marks all screen items to be deleted that are within
+ * this plane and are picture cels.
+ */
+ void deleteAllPics();
+
+public:
+ /**
+ * Marks all existing screen items matching the current
+ * picture to be deleted, then adds all cels from the
+ * new picture resource to the plane at the given
+ * position.
+ */
+ void addPic(const GuiResourceId pictureId, const Common::Point &position, const bool mirrorX);
+
+ /**
+ * If the plane is a picture plane, re-adds all cels
+ * from its picture resource to the plane. Otherwise,
+ * just clears the _pictureChanged flag.
+ */
+ void changePic();
+
+#pragma mark -
+#pragma mark Plane - Rendering
+private:
+ /**
+ * Splits all rects in the given draw list at the edges
+ * of all non-transparent planes above the current
+ * plane.
+ */
+ void breakDrawListByPlanes(DrawList &drawList, const PlaneList &planeList) const;
+
+ /**
+ * Splits all rects in the given erase list rects at the
+ * edges of all non-transparent planes above the current
+ * plane.
+ */
+ void breakEraseListByPlanes(RectList &eraseList, const PlaneList &planeList) const;
+
+ /**
+ * Synchronises changes to screen items from the current
+ * plane to the visible plane and deletes screen items
+ * from the current plane that have been marked as
+ * deleted. If `forceUpdate` is true, all screen items
+ * on the visible plane will be updated, even if they
+ * are not marked as having changed.
+ */
+ void decrementScreenItemArrayCounts(Plane *visiblePlane, const bool forceUpdate);
+
+ /**
+ * Merges the screen item from this plane at the given
+ * index into the given draw list, clipped to the given
+ * rect. TODO: Finish documenting
+ */
+ void mergeToDrawList(const DrawList::size_type index, const Common::Rect &rect, DrawList &drawList) const;
+
+ /**
+ * Adds the given rect into the given rect list,
+ * merging it with other rects already inside the list,
+ * if possible, to avoid overdraw. TODO: Finish
+ * documenting
+ */
+ void mergeToRectList(const Common::Rect &rect, RectList &rectList) const;
+
+public:
+ /**
+ * Calculates the location and dimensions of dirty rects
+ * of the screen items in this plane and adds them to
+ * the given draw and erase lists, and synchronises this
+ * plane's list of screen items to the given visible
+ * plane.
+ */
+ void calcLists(Plane &visiblePlane, const PlaneList &planeList, DrawList &drawList, RectList &eraseList);
+
+ /**
+ * TODO: Documentation
+ */
+ void filterDownEraseRects(DrawList &drawList, RectList &eraseList, RectList &transparentEraseList) const;
+
+ /**
+ * TODO: Documentation
+ */
+ void filterUpEraseRects(DrawList &drawList, RectList &eraseList) const;
+
+ /**
+ * TODO: Documentation
+ */
+ void filterUpDrawRects(DrawList &transparentDrawList, const DrawList &drawList) const;
+
+ /**
+ * Updates all of the plane's non-deleted screen items
+ * and adds them to the given draw and erase lists.
+ */
+ void redrawAll(Plane *visiblePlane, const PlaneList &planeList, DrawList &drawList, RectList &eraseList);
+
+ void remapMarkRedraw();
+};
+
+#pragma mark -
+#pragma mark PlaneList
+
+typedef Common::Array<Plane *> PlaneListBase;
+class PlaneList : public PlaneListBase {
+private:
+ inline static bool sortHelper(const Plane *a, const Plane *b) {
+ return *a < *b;
+ }
+
+ using PlaneListBase::push_back;
+
+public:
+ // A method for finding the index of a plane inside a
+ // PlaneList is used because entries in the main plane
+ // list and visible plane list of GfxFrameout are
+ // synchronised by index
+ int findIndexByObject(const reg_t object) const;
+ Plane *findByObject(const reg_t object) const;
+
+ /**
+ * Gets the priority of the top plane in the plane list.
+ */
+ int16 getTopPlanePriority() const;
+
+ /**
+ * Gets the priority of the top plane in the plane list
+ * created by a game script.
+ */
+ int16 getTopSciPlanePriority() const;
+
+ void add(Plane *plane);
+ void clear();
+ iterator erase(iterator it);
+ void erase(Plane *plane);
+ inline void sort() {
+ Common::sort(begin(), end(), sortHelper);
+ }
+ void remove_at(size_type index);
+};
+
+} // End of namespace Sci
+
+#endif
diff --git a/engines/sci/graphics/remap.cpp b/engines/sci/graphics/remap.cpp
new file mode 100644
index 0000000000..e331eaf971
--- /dev/null
+++ b/engines/sci/graphics/remap.cpp
@@ -0,0 +1,386 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 "sci/sci.h"
+#include "sci/resource.h"
+#include "sci/graphics/palette.h"
+#include "sci/graphics/palette32.h"
+#include "sci/graphics/remap.h"
+#include "sci/graphics/screen.h"
+
+namespace Sci {
+
+#pragma mark -
+#pragma mark SCI16 remapping (QFG4 demo)
+
+GfxRemap::GfxRemap(GfxPalette *palette)
+ : _palette(palette) {
+ _remapOn = false;
+ resetRemapping();
+}
+
+GfxRemap::~GfxRemap() {
+}
+
+byte GfxRemap::remapColor(byte remappedColor, byte screenColor) {
+ assert(_remapOn);
+ if (_remappingType[remappedColor] == kRemappingByRange)
+ return _remappingByRange[screenColor];
+ else if (_remappingType[remappedColor] == kRemappingByPercent)
+ return _remappingByPercent[screenColor];
+ else
+ error("remapColor(): Color %d isn't remapped", remappedColor);
+
+ return 0; // should never reach here
+}
+
+void GfxRemap::resetRemapping() {
+ _remapOn = false;
+ _remappingPercentToSet = 0;
+
+ for (int i = 0; i < 256; i++) {
+ _remappingType[i] = kRemappingNone;
+ _remappingByPercent[i] = i;
+ _remappingByRange[i] = i;
+ }
+}
+
+void GfxRemap::setRemappingPercent(byte color, byte percent) {
+ _remapOn = true;
+
+ // We need to defer the setup of the remapping table every time the screen
+ // palette is changed, so that kernelFindColor() can find the correct
+ // colors. Set it once here, in case the palette stays the same and update
+ // it on each palette change by copySysPaletteToScreen().
+ _remappingPercentToSet = percent;
+
+ for (int i = 0; i < 256; i++) {
+ byte r = _palette->_sysPalette.colors[i].r * _remappingPercentToSet / 100;
+ byte g = _palette->_sysPalette.colors[i].g * _remappingPercentToSet / 100;
+ byte b = _palette->_sysPalette.colors[i].b * _remappingPercentToSet / 100;
+ _remappingByPercent[i] = _palette->kernelFindColor(r, g, b);
+ }
+
+ _remappingType[color] = kRemappingByPercent;
+}
+
+void GfxRemap::setRemappingRange(byte color, byte from, byte to, byte base) {
+ _remapOn = true;
+
+ for (int i = from; i <= to; i++) {
+ _remappingByRange[i] = i + base;
+ }
+
+ _remappingType[color] = kRemappingByRange;
+}
+
+void GfxRemap::updateRemapping() {
+ // Check if we need to reset remapping by percent with the new colors.
+ if (_remappingPercentToSet) {
+ for (int i = 0; i < 256; i++) {
+ byte r = _palette->_sysPalette.colors[i].r * _remappingPercentToSet / 100;
+ byte g = _palette->_sysPalette.colors[i].g * _remappingPercentToSet / 100;
+ byte b = _palette->_sysPalette.colors[i].b * _remappingPercentToSet / 100;
+ _remappingByPercent[i] = _palette->kernelFindColor(r, g, b);
+ }
+ }
+}
+
+#pragma mark -
+#pragma mark SCI32 remapping
+
+#ifdef ENABLE_SCI32
+
+GfxRemap32::GfxRemap32(GfxPalette32 *palette) : _palette(palette) {
+ for (int i = 0; i < REMAP_COLOR_COUNT; i++)
+ _remaps[i] = RemapParams(0, 0, 0, 0, 100, kRemappingNone);
+ _noMapStart = _noMapCount = 0;
+ _update = false;
+ _remapCount = 0;
+
+ // The remap range was 245 - 254 in SCI2, but was changed to 235 - 244 in SCI21 middle
+ _remapEndColor = (getSciVersion() >= SCI_VERSION_2_1_MIDDLE) ? 244 : 254;
+}
+
+void GfxRemap32::remapOff(byte color) {
+ if (!color) {
+ for (int i = 0; i < REMAP_COLOR_COUNT; i++)
+ _remaps[i] = RemapParams(0, 0, 0, 0, 100, kRemappingNone);
+
+ _remapCount = 0;
+ } else {
+ assert(_remapEndColor - color >= 0 && _remapEndColor - color < REMAP_COLOR_COUNT);
+ const byte index = _remapEndColor - color;
+ _remaps[index] = RemapParams(0, 0, 0, 0, 100, kRemappingNone);
+ _remapCount--;
+ }
+
+ _update = true;
+}
+
+void GfxRemap32::setRemappingRange(byte color, byte from, byte to, byte base) {
+ assert(_remapEndColor - color >= 0 && _remapEndColor - color < REMAP_COLOR_COUNT);
+ _remaps[_remapEndColor - color] = RemapParams(from, to, base, 0, 100, kRemappingByRange);
+ initColorArrays(_remapEndColor - color);
+ _remapCount++;
+ _update = true;
+}
+
+void GfxRemap32::setRemappingPercent(byte color, byte percent) {
+ assert(_remapEndColor - color >= 0 && _remapEndColor - color < REMAP_COLOR_COUNT);
+ _remaps[_remapEndColor - color] = RemapParams(0, 0, 0, 0, percent, kRemappingByPercent);
+ initColorArrays(_remapEndColor - color);
+ _remapCount++;
+ _update = true;
+}
+
+void GfxRemap32::setRemappingToGray(byte color, byte gray) {
+ assert(_remapEndColor - color >= 0 && _remapEndColor - color < REMAP_COLOR_COUNT);
+ _remaps[_remapEndColor - color] = RemapParams(0, 0, 0, gray, 100, kRemappingToGray);
+ initColorArrays(_remapEndColor - color);
+ _remapCount++;
+ _update = true;
+}
+
+void GfxRemap32::setRemappingToPercentGray(byte color, byte gray, byte percent) {
+ assert(_remapEndColor - color >= 0 && _remapEndColor - color < REMAP_COLOR_COUNT);
+ _remaps[_remapEndColor - color] = RemapParams(0, 0, 0, gray, percent, kRemappingToPercentGray);
+ initColorArrays(_remapEndColor - color);
+ _remapCount++;
+ _update = true;
+}
+
+void GfxRemap32::setNoMatchRange(byte from, byte count) {
+ _noMapStart = from;
+ _noMapCount = count;
+}
+
+bool GfxRemap32::remapEnabled(byte color) const {
+ assert(_remapEndColor - color >= 0 && _remapEndColor - color < REMAP_COLOR_COUNT);
+ const byte index = _remapEndColor - color;
+ return (_remaps[index].type != kRemappingNone);
+}
+
+byte GfxRemap32::remapColor(byte color, byte target) {
+ assert(_remapEndColor - color >= 0 && _remapEndColor - color < REMAP_COLOR_COUNT);
+ const byte index = _remapEndColor - color;
+ if (_remaps[index].type != kRemappingNone)
+ return _remaps[index].remap[target];
+ else
+ return target;
+}
+
+void GfxRemap32::initColorArrays(byte index) {
+ Palette *curPalette = &_palette->_sysPalette;
+ RemapParams *curRemap = &_remaps[index];
+
+ memcpy(curRemap->curColor, curPalette->colors, NON_REMAPPED_COLOR_COUNT * sizeof(Color));
+ memcpy(curRemap->targetColor, curPalette->colors, NON_REMAPPED_COLOR_COUNT * sizeof(Color));
+}
+
+bool GfxRemap32::updateRemap(byte index, bool palChanged) {
+ int result;
+ RemapParams *curRemap = &_remaps[index];
+ const Palette *curPalette = &_palette->_sysPalette;
+ const Palette *nextPalette = _palette->getNextPalette();
+ bool changed = false;
+
+ if (!_update && !palChanged)
+ return false;
+
+ Common::fill(_targetChanged, _targetChanged + NON_REMAPPED_COLOR_COUNT, false);
+
+ switch (curRemap->type) {
+ case kRemappingNone:
+ return false;
+ case kRemappingByRange:
+ for (int i = 0; i < NON_REMAPPED_COLOR_COUNT; i++) {
+ if (curRemap->from <= i && i <= curRemap->to)
+ result = i + curRemap->base;
+ else
+ result = i;
+
+ if (curRemap->remap[i] != result) {
+ changed = true;
+ curRemap->remap[i] = result;
+ }
+
+ curRemap->colorChanged[i] = true;
+ }
+ return changed;
+ case kRemappingByPercent:
+ for (int i = 1; i < NON_REMAPPED_COLOR_COUNT; i++) {
+ // NOTE: This method uses nextPalette instead of curPalette
+ Color color = nextPalette->colors[i];
+
+ if (curRemap->curColor[i] != color) {
+ curRemap->colorChanged[i] = true;
+ curRemap->curColor[i] = color;
+ }
+
+ if (curRemap->percent != curRemap->oldPercent || curRemap->colorChanged[i]) {
+ byte red = CLIP<byte>(color.r * curRemap->percent / 100, 0, 255);
+ byte green = CLIP<byte>(color.g * curRemap->percent / 100, 0, 255);
+ byte blue = CLIP<byte>(color.b * curRemap->percent / 100, 0, 255);
+ byte used = curRemap->targetColor[i].used;
+
+ Color newColor = { used, red, green, blue };
+ if (curRemap->targetColor[i] != newColor) {
+ _targetChanged[i] = true;
+ curRemap->targetColor[i] = newColor;
+ }
+ }
+ }
+
+ changed = applyRemap(index);
+ Common::fill(curRemap->colorChanged, curRemap->colorChanged + NON_REMAPPED_COLOR_COUNT, false);
+ curRemap->oldPercent = curRemap->percent;
+ return changed;
+ case kRemappingToGray:
+ for (int i = 1; i < NON_REMAPPED_COLOR_COUNT; i++) {
+ Color color = curPalette->colors[i];
+
+ if (curRemap->curColor[i] != color) {
+ curRemap->colorChanged[i] = true;
+ curRemap->curColor[i] = color;
+ }
+
+ if (curRemap->gray != curRemap->oldGray || curRemap->colorChanged[i]) {
+ byte lumosity = ((color.r * 77) + (color.g * 151) + (color.b * 28)) >> 8;
+ byte red = CLIP<byte>(color.r - ((color.r - lumosity) * curRemap->gray / 100), 0, 255);
+ byte green = CLIP<byte>(color.g - ((color.g - lumosity) * curRemap->gray / 100), 0, 255);
+ byte blue = CLIP<byte>(color.b - ((color.b - lumosity) * curRemap->gray / 100), 0, 255);
+ byte used = curRemap->targetColor[i].used;
+
+ Color newColor = { used, red, green, blue };
+ if (curRemap->targetColor[i] != newColor) {
+ _targetChanged[i] = true;
+ curRemap->targetColor[i] = newColor;
+ }
+ }
+ }
+
+ changed = applyRemap(index);
+ Common::fill(curRemap->colorChanged, curRemap->colorChanged + NON_REMAPPED_COLOR_COUNT, false);
+ curRemap->oldGray = curRemap->gray;
+ return changed;
+ case kRemappingToPercentGray:
+ for (int i = 1; i < NON_REMAPPED_COLOR_COUNT; i++) {
+ Color color = curPalette->colors[i];
+
+ if (curRemap->curColor[i] != color) {
+ curRemap->colorChanged[i] = true;
+ curRemap->curColor[i] = color;
+ }
+
+ if (curRemap->percent != curRemap->oldPercent || curRemap->gray != curRemap->oldGray || curRemap->colorChanged[i]) {
+ byte lumosity = ((color.r * 77) + (color.g * 151) + (color.b * 28)) >> 8;
+ lumosity = lumosity * curRemap->percent / 100;
+ byte red = CLIP<byte>(color.r - ((color.r - lumosity) * curRemap->gray / 100), 0, 255);
+ byte green = CLIP<byte>(color.g - ((color.g - lumosity) * curRemap->gray / 100), 0, 255);
+ byte blue = CLIP<byte>(color.b - ((color.b - lumosity) * curRemap->gray / 100), 0, 255);
+ byte used = curRemap->targetColor[i].used;
+
+ Color newColor = { used, red, green, blue };
+ if (curRemap->targetColor[i] != newColor) {
+ _targetChanged[i] = true;
+ curRemap->targetColor[i] = newColor;
+ }
+ }
+ }
+
+ changed = applyRemap(index);
+ Common::fill(curRemap->colorChanged, curRemap->colorChanged + NON_REMAPPED_COLOR_COUNT, false);
+ curRemap->oldPercent = curRemap->percent;
+ curRemap->oldGray = curRemap->gray;
+ return changed;
+ default:
+ return false;
+ }
+}
+
+static int colorDistance(Color a, Color b) {
+ int rDiff = (a.r - b.r) * (a.r - b.r);
+ int gDiff = (a.g - b.g) * (a.g - b.g);
+ int bDiff = (a.b - b.b) * (a.b - b.b);
+ return rDiff + gDiff + bDiff;
+}
+
+bool GfxRemap32::applyRemap(byte index) {
+ RemapParams *curRemap = &_remaps[index];
+ const bool *cycleMap = _palette->getCyclemap();
+ bool unmappedColors[NON_REMAPPED_COLOR_COUNT];
+ Color newColors[NON_REMAPPED_COLOR_COUNT];
+ bool changed = false;
+
+ Common::fill(unmappedColors, unmappedColors + NON_REMAPPED_COLOR_COUNT, false);
+ if (_noMapCount)
+ Common::fill(unmappedColors + _noMapStart, unmappedColors + _noMapStart + _noMapCount, true);
+
+ for (int i = 0; i < NON_REMAPPED_COLOR_COUNT; i++) {
+ if (cycleMap[i])
+ unmappedColors[i] = true;
+ }
+
+ int curColor = 0;
+ for (int i = 1; i < NON_REMAPPED_COLOR_COUNT; i++) {
+ if (curRemap->colorChanged[i] && !unmappedColors[i])
+ newColors[curColor++] = curRemap->curColor[i];
+ }
+
+ for (int i = 1; i < NON_REMAPPED_COLOR_COUNT; i++) {
+ Color targetColor = curRemap->targetColor[i];
+ bool colorChanged = curRemap->colorChanged[curRemap->remap[i]];
+
+ if (!_targetChanged[i] && !colorChanged)
+ continue;
+
+ if (_targetChanged[i] && colorChanged)
+ if (curRemap->distance[i] < 100 && colorDistance(targetColor, curRemap->curColor[curRemap->remap[i]]) <= curRemap->distance[i])
+ continue;
+
+ int diff = 0;
+ int16 result = _palette->matchColor(targetColor.r, targetColor.g, targetColor.b, curRemap->distance[i], diff, unmappedColors);
+ if (result != -1 && curRemap->remap[i] != result) {
+ changed = true;
+ curRemap->remap[i] = result;
+ curRemap->distance[i] = diff;
+ }
+ }
+
+ return changed;
+}
+
+bool GfxRemap32::remapAllTables(bool palChanged) {
+ bool changed = false;
+
+ for (int i = 0; i < REMAP_COLOR_COUNT; i++) {
+ changed |= updateRemap(i, palChanged);
+ }
+
+ _update = false;
+ return changed;
+}
+
+#endif
+
+} // End of namespace Sci
diff --git a/engines/sci/graphics/remap.h b/engines/sci/graphics/remap.h
new file mode 100644
index 0000000000..d012568f7f
--- /dev/null
+++ b/engines/sci/graphics/remap.h
@@ -0,0 +1,154 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 SCI_GRAPHICS_REMAP_H
+#define SCI_GRAPHICS_REMAP_H
+
+#include "common/array.h"
+#include "sci/graphics/helpers.h"
+
+namespace Sci {
+
+class GfxScreen;
+
+enum ColorRemappingType {
+ kRemappingNone = 0,
+ kRemappingByRange = 1,
+ kRemappingByPercent = 2,
+ kRemappingToGray = 3,
+ kRemappingToPercentGray = 4
+};
+
+#define REMAP_COLOR_COUNT 9
+#define NON_REMAPPED_COLOR_COUNT 236
+
+/**
+ * Remap class, handles color remapping
+ */
+class GfxRemap {
+public:
+ GfxRemap(GfxPalette *_palette);
+ ~GfxRemap();
+
+ void resetRemapping();
+ void setRemappingPercent(byte color, byte percent);
+ void setRemappingRange(byte color, byte from, byte to, byte base);
+ bool isRemapped(byte color) const {
+ return _remapOn && (_remappingType[color] != kRemappingNone);
+ }
+ byte remapColor(byte remappedColor, byte screenColor);
+ void updateRemapping();
+
+private:
+ GfxScreen *_screen;
+ GfxPalette *_palette;
+
+ bool _remapOn;
+ ColorRemappingType _remappingType[256];
+ byte _remappingByPercent[256];
+ byte _remappingByRange[256];
+ uint16 _remappingPercentToSet;
+};
+
+#ifdef ENABLE_SCI32
+
+struct RemapParams {
+ byte from;
+ byte to;
+ byte base;
+ byte gray;
+ byte oldGray;
+ byte percent;
+ byte oldPercent;
+ ColorRemappingType type;
+ Color curColor[256];
+ Color targetColor[256];
+ byte distance[256];
+ byte remap[256];
+ bool colorChanged[256];
+
+ RemapParams() {
+ from = to = base = gray = oldGray = percent = oldPercent = 0;
+ type = kRemappingNone;
+
+ // curColor and targetColor are initialized in GfxRemap32::initColorArrays
+ memset(curColor, 0, 256 * sizeof(Color));
+ memset(targetColor, 0, 256 * sizeof(Color));
+ memset(distance, 0, 256);
+ for (int i = 0; i < NON_REMAPPED_COLOR_COUNT; i++)
+ remap[i] = i;
+ Common::fill(colorChanged, colorChanged + ARRAYSIZE(colorChanged), true);
+ }
+
+ RemapParams(byte from_, byte to_, byte base_, byte gray_, byte percent_, ColorRemappingType type_) {
+ from = from_;
+ to = to_;
+ base = base_;
+ gray = oldGray = gray_;
+ percent = oldPercent = percent_;
+ type = type_;
+
+ // curColor and targetColor are initialized in GfxRemap32::initColorArrays
+ memset(curColor, 0, 256 * sizeof(Color));
+ memset(targetColor, 0, 256 * sizeof(Color));
+ memset(distance, 0, 256);
+ for (int i = 0; i < NON_REMAPPED_COLOR_COUNT; i++)
+ remap[i] = i;
+ Common::fill(colorChanged, colorChanged + ARRAYSIZE(colorChanged), true);
+ }
+};
+
+class GfxRemap32 {
+public:
+ GfxRemap32(GfxPalette32 *palette);
+ ~GfxRemap32() {}
+
+ void remapOff(byte color);
+ void setRemappingRange(byte color, byte from, byte to, byte base);
+ void setRemappingPercent(byte color, byte percent);
+ void setRemappingToGray(byte color, byte gray);
+ void setRemappingToPercentGray(byte color, byte gray, byte percent);
+ void setNoMatchRange(byte from, byte count);
+ bool remapEnabled(byte color) const;
+ byte remapColor(byte color, byte target);
+ bool remapAllTables(bool palChanged);
+ int getRemapCount() const { return _remapCount; }
+ int getStartColor() const { return _remapEndColor - REMAP_COLOR_COUNT + 1; }
+ int getEndColor() const { return _remapEndColor; }
+private:
+ GfxPalette32 *_palette;
+ RemapParams _remaps[REMAP_COLOR_COUNT];
+ bool _update;
+ byte _noMapStart, _noMapCount;
+ bool _targetChanged[NON_REMAPPED_COLOR_COUNT];
+ byte _remapEndColor;
+ int _remapCount;
+
+ void initColorArrays(byte index);
+ bool applyRemap(byte index);
+ bool updateRemap(byte index, bool palChanged);
+};
+#endif
+
+} // End of namespace Sci
+
+#endif
diff --git a/engines/sci/graphics/screen.cpp b/engines/sci/graphics/screen.cpp
index 4cd6344600..c977a93817 100644
--- a/engines/sci/graphics/screen.cpp
+++ b/engines/sci/graphics/screen.cpp
@@ -214,36 +214,6 @@ GfxScreen::GfxScreen(ResourceManager *resMan) : _resMan(resMan) {
error("Unknown SCI1.1 Mac game");
} else
initGraphics(_displayWidth, _displayHeight, _displayWidth > 320);
-
- // Initialize code pointers
- _vectorAdjustCoordinatePtr = &GfxScreen::vectorAdjustCoordinateNOP;
- _vectorAdjustLineCoordinatesPtr = &GfxScreen::vectorAdjustLineCoordinatesNOP;
- _vectorIsFillMatchPtr = &GfxScreen::vectorIsFillMatchNormal;
- _vectorPutPixelPtr = &GfxScreen::putPixelNormal;
- _vectorPutLinePixelPtr = &GfxScreen::putPixel;
- _vectorGetPixelPtr = &GfxScreen::getPixelNormal;
- _putPixelPtr = &GfxScreen::putPixelNormal;
- _getPixelPtr = &GfxScreen::getPixelNormal;
-
- switch (_upscaledHires) {
- case GFX_SCREEN_UPSCALED_480x300:
- _vectorAdjustCoordinatePtr = &GfxScreen::vectorAdjustCoordinate480x300Mac;
- _vectorAdjustLineCoordinatesPtr = &GfxScreen::vectorAdjustLineCoordinates480x300Mac;
- // vectorPutPixel -> we already adjust coordinates for vector code, that's why we can set pixels directly
- // vectorGetPixel -> see vectorPutPixel
- _vectorPutLinePixelPtr = &GfxScreen::vectorPutLinePixel480x300Mac;
- _putPixelPtr = &GfxScreen::putPixelAllUpscaled;
- _getPixelPtr = &GfxScreen::getPixelUpscaled;
- break;
- case GFX_SCREEN_UPSCALED_640x400:
- case GFX_SCREEN_UPSCALED_640x440:
- case GFX_SCREEN_UPSCALED_640x480:
- _vectorPutPixelPtr = &GfxScreen::putPixelDisplayUpscaled;
- _putPixelPtr = &GfxScreen::putPixelDisplayUpscaled;
- break;
- case GFX_SCREEN_UPSCALED_DISABLED:
- break;
- }
}
GfxScreen::~GfxScreen() {
@@ -270,17 +240,26 @@ void GfxScreen::copyToScreen() {
}
void GfxScreen::copyFromScreen(byte *buffer) {
- // TODO this ignores the pitch
Graphics::Surface *screen = g_system->lockScreen();
- memcpy(buffer, screen->getPixels(), _displayPixels);
+
+ if (screen->pitch == _displayWidth) {
+ memcpy(buffer, screen->getPixels(), _displayPixels);
+ } else {
+ const byte *src = (const byte *)screen->getPixels();
+ uint height = _displayHeight;
+
+ while (height--) {
+ memcpy(buffer, src, _displayWidth);
+ buffer += _displayWidth;
+ src += screen->pitch;
+ }
+ }
+
g_system->unlockScreen();
}
void GfxScreen::kernelSyncWithFramebuffer() {
- // TODO this ignores the pitch
- Graphics::Surface *screen = g_system->lockScreen();
- memcpy(_displayScreen, screen->getPixels(), _displayPixels);
- g_system->unlockScreen();
+ copyFromScreen(_displayScreen);
}
void GfxScreen::copyRectToScreen(const Common::Rect &rect) {
@@ -325,40 +304,68 @@ byte GfxScreen::getDrawingMask(byte color, byte prio, byte control) {
return flag;
}
-void GfxScreen::vectorAdjustCoordinateNOP(int16 *x, int16 *y) {
+void GfxScreen::vectorAdjustLineCoordinates(int16 *left, int16 *top, int16 *right, int16 *bottom, byte drawMask, byte color, byte priority, byte control) {
+ switch (_upscaledHires) {
+ case GFX_SCREEN_UPSCALED_480x300: {
+ int16 displayLeft = (*left * 3) / 2;
+ int16 displayRight = (*right * 3) / 2;
+ int16 displayTop = (*top * 3) / 2;
+ int16 displayBottom = (*bottom * 3) / 2;
+
+ if (displayLeft < displayRight) {
+ // one more pixel to the left, one more pixel to the right
+ if (displayLeft > 0)
+ vectorPutLinePixel(displayLeft - 1, displayTop, drawMask, color, priority, control);
+ vectorPutLinePixel(displayRight + 1, displayBottom, drawMask, color, priority, control);
+ } else if (displayLeft > displayRight) {
+ if (displayRight > 0)
+ vectorPutLinePixel(displayRight - 1, displayBottom, drawMask, color, priority, control);
+ vectorPutLinePixel(displayLeft + 1, displayTop, drawMask, color, priority, control);
+ }
+ *left = displayLeft;
+ *top = displayTop;
+ *right = displayRight;
+ *bottom = displayBottom;
+ break;
+ }
+ default:
+ break;
+ }
}
-void GfxScreen::vectorAdjustCoordinate480x300Mac(int16 *x, int16 *y) {
- *x = _upscaledWidthMapping[*x];
- *y = _upscaledHeightMapping[*y];
+// This is called from vector drawing to put a pixel at a certain location
+void GfxScreen::vectorPutLinePixel(int16 x, int16 y, byte drawMask, byte color, byte priority, byte control) {
+ if (_upscaledHires == GFX_SCREEN_UPSCALED_480x300) {
+ vectorPutLinePixel480x300(x, y, drawMask, color, priority, control);
+ return;
+ }
+
+ // For anything else forward to the regular putPixel
+ putPixel(x, y, drawMask, color, priority, control);
}
-void GfxScreen::vectorAdjustLineCoordinatesNOP(int16 *left, int16 *top, int16 *right, int16 *bottom, byte drawMask, byte color, byte priority, byte control) {
+// Special 480x300 Mac putPixel for vector line drawing, also draws an additional pixel below the actual one
+void GfxScreen::vectorPutLinePixel480x300(int16 x, int16 y, byte drawMask, byte color, byte priority, byte control) {
+ int offset = y * _width + x;
+
+ if (drawMask & GFX_SCREEN_MASK_VISUAL) {
+ // also set pixel below actual pixel
+ _visualScreen[offset] = color;
+ _visualScreen[offset + _width] = color;
+ _displayScreen[offset] = color;
+ _displayScreen[offset + _displayWidth] = color;
+ }
+ if (drawMask & GFX_SCREEN_MASK_PRIORITY) {
+ _priorityScreen[offset] = priority;
+ _priorityScreen[offset + _width] = priority;
+ }
+ if (drawMask & GFX_SCREEN_MASK_CONTROL) {
+ _controlScreen[offset] = control;
+ _controlScreen[offset + _width] = control;
+ }
}
-void GfxScreen::vectorAdjustLineCoordinates480x300Mac(int16 *left, int16 *top, int16 *right, int16 *bottom, byte drawMask, byte color, byte priority, byte control) {
- int16 displayLeft = _upscaledWidthMapping[*left];
- int16 displayRight = _upscaledWidthMapping[*right];
- int16 displayTop = _upscaledHeightMapping[*top];
- int16 displayBottom = _upscaledHeightMapping[*bottom];
-
- if (displayLeft < displayRight) {
- // one more pixel to the left, one more pixel to the right
- if (displayLeft > 0)
- vectorPutLinePixel(displayLeft - 1, displayTop, drawMask, color, priority, control);
- vectorPutLinePixel(displayRight + 1, displayBottom, drawMask, color, priority, control);
- } else if (displayLeft > displayRight) {
- if (displayRight > 0)
- vectorPutLinePixel(displayRight - 1, displayBottom, drawMask, color, priority, control);
- vectorPutLinePixel(displayLeft + 1, displayTop, drawMask, color, priority, control);
- }
- *left = displayLeft;
- *top = displayTop;
- *right = displayRight;
- *bottom = displayBottom;
-}
-
-byte GfxScreen::vectorIsFillMatchNormal(int16 x, int16 y, byte screenMask, byte checkForColor, byte checkForPriority, byte checkForControl, bool isEGA) {
+byte GfxScreen::vectorIsFillMatch(int16 x, int16 y, byte screenMask, byte checkForColor, byte checkForPriority, byte checkForControl, bool isEGA) {
int offset = y * _width + x;
byte match = 0;
@@ -388,132 +395,6 @@ byte GfxScreen::vectorIsFillMatchNormal(int16 x, int16 y, byte screenMask, byte
return match;
}
-// Special 480x300 Mac putPixel for vector line drawing, also draws an additional pixel below the actual one
-void GfxScreen::vectorPutLinePixel480x300Mac(int16 x, int16 y, byte drawMask, byte color, byte priority, byte control) {
- int offset = y * _width + x;
-
- if (drawMask & GFX_SCREEN_MASK_VISUAL) {
- _visualScreen[offset] = color;
- _visualScreen[offset + _width] = color;
- _displayScreen[offset] = color;
- // also set pixel below actual pixel
- _displayScreen[offset + _displayWidth] = color;
- }
- if (drawMask & GFX_SCREEN_MASK_PRIORITY) {
- _priorityScreen[offset] = priority;
- _priorityScreen[offset + _width] = priority;
- }
- if (drawMask & GFX_SCREEN_MASK_CONTROL) {
- _controlScreen[offset] = control;
- _controlScreen[offset + _width] = control;
- }
-}
-
-// Directly sets a pixel on various screens, display is not upscaled
-void GfxScreen::putPixelNormal(int16 x, int16 y, byte drawMask, byte color, byte priority, byte control) {
- int offset = y * _width + x;
-
- if (drawMask & GFX_SCREEN_MASK_VISUAL) {
- _visualScreen[offset] = color;
- _displayScreen[offset] = color;
- }
- if (drawMask & GFX_SCREEN_MASK_PRIORITY)
- _priorityScreen[offset] = priority;
- if (drawMask & GFX_SCREEN_MASK_CONTROL)
- _controlScreen[offset] = control;
-}
-
-// Directly sets a pixel on various screens, display IS upscaled
-void GfxScreen::putPixelDisplayUpscaled(int16 x, int16 y, byte drawMask, byte color, byte priority, byte control) {
- int offset = y * _width + x;
-
- if (drawMask & GFX_SCREEN_MASK_VISUAL) {
- _visualScreen[offset] = color;
- putScaledPixelOnScreen(_displayScreen, x, y, color);
- }
- if (drawMask & GFX_SCREEN_MASK_PRIORITY)
- _priorityScreen[offset] = priority;
- if (drawMask & GFX_SCREEN_MASK_CONTROL)
- _controlScreen[offset] = control;
-}
-
-// Directly sets a pixel on various screens, ALL screens ARE upscaled
-void GfxScreen::putPixelAllUpscaled(int16 x, int16 y, byte drawMask, byte color, byte priority, byte control) {
- if (drawMask & GFX_SCREEN_MASK_VISUAL) {
- putScaledPixelOnScreen(_visualScreen, x, y, color);
- putScaledPixelOnScreen(_displayScreen, x, y, color);
- }
- if (drawMask & GFX_SCREEN_MASK_PRIORITY)
- putScaledPixelOnScreen(_priorityScreen, x, y, priority);
- if (drawMask & GFX_SCREEN_MASK_CONTROL)
- putScaledPixelOnScreen(_controlScreen, x, y, control);
-}
-
-/**
- * This is used to put font pixels onto the screen - we adjust differently, so that we won't
- * do triple pixel lines in any case on upscaled hires. That way the font will not get distorted
- * Sierra SCI didn't do this
- */
-void GfxScreen::putFontPixel(int16 startingY, int16 x, int16 y, byte color) {
- int16 actualY = startingY + y;
- if (_fontIsUpscaled) {
- // Do not scale ourselves, but put it on the display directly
- putPixelOnDisplay(x, actualY, color);
- } else {
- int offset = actualY * _width + x;
-
- _visualScreen[offset] = color;
- switch (_upscaledHires) {
- case GFX_SCREEN_UPSCALED_DISABLED:
- _displayScreen[offset] = color;
- break;
- case GFX_SCREEN_UPSCALED_640x400:
- case GFX_SCREEN_UPSCALED_640x440:
- case GFX_SCREEN_UPSCALED_640x480: {
- // to 1-> 4 pixels upscaling for all of those, so that fonts won't look weird
- int displayOffset = (_upscaledHeightMapping[startingY] + y * 2) * _displayWidth + x * 2;
- _displayScreen[displayOffset] = color;
- _displayScreen[displayOffset + 1] = color;
- displayOffset += _displayWidth;
- _displayScreen[displayOffset] = color;
- _displayScreen[displayOffset + 1] = color;
- break;
- }
- default:
- putScaledPixelOnScreen(_displayScreen, x, actualY, color);
- break;
- }
- }
-}
-
-/**
- * This will just change a pixel directly on displayscreen. It is supposed to be
- * only used on upscaled-Hires games where hires content needs to get drawn ONTO
- * the upscaled display screen (like japanese fonts, hires portraits, etc.).
- */
-void GfxScreen::putPixelOnDisplay(int16 x, int16 y, byte color) {
- int offset = y * _displayWidth + x;
- _displayScreen[offset] = color;
-}
-
-//void GfxScreen::putScaledPixelOnDisplay(int16 x, int16 y, byte color) {
-//}
-
-void GfxScreen::putScaledPixelOnScreen(byte *screen, int16 x, int16 y, byte data) {
- int displayOffset = _upscaledHeightMapping[y] * _displayWidth + _upscaledWidthMapping[x];
- int heightOffsetBreak = (_upscaledHeightMapping[y + 1] - _upscaledHeightMapping[y]) * _displayWidth;
- int heightOffset = 0;
- int widthOffsetBreak = _upscaledWidthMapping[x + 1] - _upscaledWidthMapping[x];
- do {
- int widthOffset = 0;
- do {
- screen[displayOffset + heightOffset + widthOffset] = data;
- widthOffset++;
- } while (widthOffset != widthOffsetBreak);
- heightOffset += _displayWidth;
- } while (heightOffset != heightOffsetBreak);
-}
-
/**
* Sierra's Bresenham line drawing.
* WARNING: Do not replace this with Graphics::drawLine(), as this causes issues
@@ -595,16 +476,6 @@ void GfxScreen::putKanjiChar(Graphics::FontSJIS *commonFont, int16 x, int16 y, u
commonFont->drawChar(displayPtr, chr, _displayWidth, 1, color, 0, -1, -1);
}
-byte GfxScreen::getPixelNormal(byte *screen, int16 x, int16 y) {
- return screen[y * _width + x];
-}
-
-byte GfxScreen::getPixelUpscaled(byte *screen, int16 x, int16 y) {
- int16 mappedX = _upscaledWidthMapping[x];
- int16 mappedY = _upscaledHeightMapping[y];
- return screen[mappedY * _width + mappedX];
-}
-
int GfxScreen::bitsGetDataSize(Common::Rect rect, byte mask) {
int byteCount = sizeof(rect) + sizeof(mask);
int pixels = rect.width() * rect.height();
@@ -615,7 +486,7 @@ int GfxScreen::bitsGetDataSize(Common::Rect rect, byte mask) {
} else {
int rectHeight = _upscaledHeightMapping[rect.bottom] - _upscaledHeightMapping[rect.top];
int rectWidth = _upscaledWidthMapping[rect.right] - _upscaledWidthMapping[rect.left];
- byteCount += rectHeight * rect.width() * rectWidth; // _displayScreen (upscaled hires)
+ byteCount += rectHeight * rectWidth; // _displayScreen (upscaled hires)
}
}
if (mask & GFX_SCREEN_MASK_PRIORITY) {
@@ -629,7 +500,6 @@ int GfxScreen::bitsGetDataSize(Common::Rect rect, byte mask) {
error("bitsGetDataSize() called w/o being in upscaled hires mode");
byteCount += pixels; // _displayScreen (coordinates actually are given to us for hires displayScreen)
}
-
return byteCount;
}
@@ -796,7 +666,7 @@ void GfxScreen::dither(bool addToFlag) {
*displayPtr = color;
break;
default:
- putScaledPixelOnScreen(_displayScreen, x, y, color);
+ putScaledPixelOnDisplay(x, y, color);
break;
}
*visualPtr = color;
@@ -828,7 +698,7 @@ void GfxScreen::dither(bool addToFlag) {
*displayPtr = ditheredColor;
break;
default:
- putScaledPixelOnScreen(_displayScreen, x, y, ditheredColor);
+ putScaledPixelOnDisplay(x, y, ditheredColor);
break;
}
color = ((x^y) & 1) ? color >> 4 : color & 0x0F;
diff --git a/engines/sci/graphics/screen.h b/engines/sci/graphics/screen.h
index 1c946ef02f..65416252f6 100644
--- a/engines/sci/graphics/screen.h
+++ b/engines/sci/graphics/screen.h
@@ -76,6 +76,11 @@ public:
byte getColorWhite() { return _colorWhite; }
byte getColorDefaultVectorData() { return _colorDefaultVectorData; }
+#ifdef ENABLE_SCI32
+ byte *getDisplayScreen() { return _displayScreen; }
+ byte *getPriorityScreen() { return _priorityScreen; }
+#endif
+
void clearForRestoreGame();
void copyToScreen();
void copyFromScreen(byte *buffer);
@@ -84,51 +89,16 @@ public:
void copyDisplayRectToScreen(const Common::Rect &rect);
void copyRectToScreen(const Common::Rect &rect, int16 x, int16 y);
- // calls to code pointers
- void inline vectorAdjustCoordinate (int16 *x, int16 *y) {
- (this->*_vectorAdjustCoordinatePtr)(x, y);
- }
- void inline vectorAdjustLineCoordinates (int16 *left, int16 *top, int16 *right, int16 *bottom, byte drawMask, byte color, byte priority, byte control) {
- (this->*_vectorAdjustLineCoordinatesPtr)(left, top, right, bottom, drawMask, color, priority, control);
- }
- byte inline vectorIsFillMatch (int16 x, int16 y, byte screenMask, byte t_color, byte t_pri, byte t_con, bool isEGA) {
- return (this->*_vectorIsFillMatchPtr)(x, y, screenMask, t_color, t_pri, t_con, isEGA);
- }
- void inline vectorPutPixel(int16 x, int16 y, byte drawMask, byte color, byte priority, byte control) {
- (this->*_vectorPutPixelPtr)(x, y, drawMask, color, priority, control);
- }
- void inline vectorPutLinePixel(int16 x, int16 y, byte drawMask, byte color, byte priority, byte control) {
- (this->*_vectorPutLinePixelPtr)(x, y, drawMask, color, priority, control);
- }
- byte inline vectorGetVisual(int16 x, int16 y) {
- return (this->*_vectorGetPixelPtr)(_visualScreen, x, y);
- }
- byte inline vectorGetPriority(int16 x, int16 y) {
- return (this->*_vectorGetPixelPtr)(_priorityScreen, x, y);
- }
- byte inline vectorGetControl(int16 x, int16 y) {
- return (this->*_vectorGetPixelPtr)(_controlScreen, x, y);
- }
-
-
- void inline putPixel(int16 x, int16 y, byte drawMask, byte color, byte priority, byte control) {
- (this->*_putPixelPtr)(x, y, drawMask, color, priority, control);
- }
+ // Vector drawing
+private:
+ void vectorPutLinePixel(int16 x, int16 y, byte drawMask, byte color, byte priority, byte control);
+ void vectorPutLinePixel480x300(int16 x, int16 y, byte drawMask, byte color, byte priority, byte control);
- byte inline getVisual(int16 x, int16 y) {
- return (this->*_getPixelPtr)(_visualScreen, x, y);
- }
- byte inline getPriority(int16 x, int16 y) {
- return (this->*_getPixelPtr)(_priorityScreen, x, y);
- }
- byte inline getControl(int16 x, int16 y) {
- return (this->*_getPixelPtr)(_controlScreen, x, y);
- }
+public:
+ void vectorAdjustLineCoordinates(int16 *left, int16 *top, int16 *right, int16 *bottom, byte drawMask, byte color, byte priority, byte control);
+ byte vectorIsFillMatch(int16 x, int16 y, byte screenMask, byte checkForColor, byte checkForPriority, byte checkForControl, bool isEGA);
byte getDrawingMask(byte color, byte prio, byte control);
- //void putPixel(int16 x, int16 y, byte drawMask, byte color, byte prio, byte control);
- void putFontPixel(int16 startingY, int16 x, int16 y, byte color);
- void putPixelOnDisplay(int16 x, int16 y, byte color);
void drawLine(Common::Point startPoint, Common::Point endPoint, byte color, byte prio, byte control);
void drawLine(int16 left, int16 top, int16 right, int16 bottom, byte color, byte prio, byte control) {
drawLine(Common::Point(left, top), Common::Point(right, bottom), color, prio, control);
@@ -206,8 +176,8 @@ private:
byte *_controlScreen;
/**
- * This screen is the one that is actually displayed to the user. It may be
- * 640x400 for japanese SCI1 games. SCI0 games may be undithered in here.
+ * This screen is the one, where pixels are copied out of into the frame buffer.
+ * It may be 640x400 for japanese SCI1 games. SCI0 games may be undithered in here.
* Only read from this buffer for Save/ShowBits usage.
*/
byte *_displayScreen;
@@ -215,8 +185,8 @@ private:
ResourceManager *_resMan;
/**
- * Pointer to the currently active screen (changing it only required for
- * debug purposes).
+ * Pointer to the currently active screen (changing only required for
+ * debug purposes, to show for example the priority screen).
*/
byte *_activeScreen;
@@ -239,38 +209,241 @@ private:
*/
bool _fontIsUpscaled;
- // dynamic code
- void (GfxScreen::*_vectorAdjustCoordinatePtr) (int16 *x, int16 *y);
- void vectorAdjustCoordinateNOP (int16 *x, int16 *y);
- void vectorAdjustCoordinate480x300Mac (int16 *x, int16 *y);
- void (GfxScreen::*_vectorAdjustLineCoordinatesPtr) (int16 *left, int16 *top, int16 *right, int16 *bottom, byte drawMask, byte color, byte priority, byte control);
- void vectorAdjustLineCoordinatesNOP (int16 *left, int16 *top, int16 *right, int16 *bottom, byte drawMask, byte color, byte priority, byte control);
- void vectorAdjustLineCoordinates480x300Mac (int16 *left, int16 *top, int16 *right, int16 *bottom, byte drawMask, byte color, byte priority, byte control);
-
- byte (GfxScreen::*_vectorIsFillMatchPtr) (int16 x, int16 y, byte screenMask, byte t_color, byte t_pri, byte t_con, bool isEGA);
- byte vectorIsFillMatchNormal (int16 x, int16 y, byte screenMask, byte t_color, byte t_pri, byte t_con, bool isEGA);
- byte vectorIsFillMatch480x300Mac (int16 x, int16 y, byte screenMask, byte t_color, byte t_pri, byte t_con, bool isEGA);
+ // pixel related code, in header so that it can be inlined for performance
+public:
+ void putPixel(int16 x, int16 y, byte drawMask, byte color, byte priority, byte control) {
+ if (_upscaledHires == GFX_SCREEN_UPSCALED_480x300) {
+ putPixel480x300(x, y, drawMask, color, priority, control);
+ return;
+ }
+
+ // Set pixel for visual, priority and control map directly, those are not upscaled
+ int offset = y * _width + x;
+
+ if (drawMask & GFX_SCREEN_MASK_VISUAL) {
+ _visualScreen[offset] = color;
+
+ int displayOffset = 0;
+
+ switch (_upscaledHires) {
+ case GFX_SCREEN_UPSCALED_DISABLED:
+ displayOffset = offset;
+ _displayScreen[displayOffset] = color;
+ break;
+
+ case GFX_SCREEN_UPSCALED_640x400:
+ case GFX_SCREEN_UPSCALED_640x440:
+ case GFX_SCREEN_UPSCALED_640x480:
+ putScaledPixelOnDisplay(x, y, color);
+ break;
+ default:
+ break;
+ }
+ }
+ if (drawMask & GFX_SCREEN_MASK_PRIORITY) {
+ _priorityScreen[offset] = priority;
+ }
+ if (drawMask & GFX_SCREEN_MASK_CONTROL) {
+ _controlScreen[offset] = control;
+ }
+ }
- void (GfxScreen::*_vectorPutPixelPtr) (int16 x, int16 y, byte drawMask, byte color, byte priority, byte control);
- void vectorPutPixel480x300Mac (int16 x, int16 y, byte drawMask, byte color, byte priority, byte control);
+ void putPixel480x300(int16 x, int16 y, byte drawMask, byte color, byte priority, byte control) {
+ int offset = ((y * 3) / 2 * _width) + ((x * 3) / 2);
+
+ // All maps are upscaled
+ // TODO: figure out, what Sierra exactly did on Mac for these games
+ if (drawMask & GFX_SCREEN_MASK_VISUAL) {
+ putPixel480x300Worker(x, y, offset, _visualScreen, color);
+ putPixel480x300Worker(x, y, offset, _displayScreen, color);
+ }
+ if (drawMask & GFX_SCREEN_MASK_PRIORITY) {
+ putPixel480x300Worker(x, y, offset, _priorityScreen, priority);
+ }
+ if (drawMask & GFX_SCREEN_MASK_CONTROL) {
+ putPixel480x300Worker(x, y, offset, _controlScreen, control);
+ }
+ }
+ void putPixel480x300Worker(int16 x, int16 y, int offset, byte *screen, byte byteToSet) {
+ screen[offset] = byteToSet;
+ if (x & 1)
+ screen[offset + 1] = byteToSet;
+ if (y & 1)
+ screen[offset + _width] = byteToSet;
+ if ((x & 1) && (y & 1))
+ screen[offset + _width + 1] = byteToSet;
+ }
- void (GfxScreen::*_vectorPutLinePixelPtr) (int16 x, int16 y, byte drawMask, byte color, byte priority, byte control);
- void vectorPutLinePixel480x300Mac (int16 x, int16 y, byte drawMask, byte color, byte priority, byte control);
+ // This is called from vector drawing to put a pixel at a certain location
+ void vectorPutPixel(int16 x, int16 y, byte drawMask, byte color, byte priority, byte control) {
+ switch (_upscaledHires) {
+ case GFX_SCREEN_UPSCALED_640x400:
+ case GFX_SCREEN_UPSCALED_640x440:
+ case GFX_SCREEN_UPSCALED_640x480:
+ // For regular upscaled modes forward to the regular putPixel
+ putPixel(x, y, drawMask, color, priority, control);
+ return;
+ break;
+
+ default:
+ break;
+ }
+
+ // For non-upscaled mode and 480x300 Mac put pixels directly
+ int offset = y * _width + x;
+
+ if (drawMask & GFX_SCREEN_MASK_VISUAL) {
+ _visualScreen[offset] = color;
+ _displayScreen[offset] = color;
+ }
+ if (drawMask & GFX_SCREEN_MASK_PRIORITY) {
+ _priorityScreen[offset] = priority;
+ }
+ if (drawMask & GFX_SCREEN_MASK_CONTROL) {
+ _controlScreen[offset] = control;
+ }
+ }
- byte (GfxScreen::*_vectorGetPixelPtr) (byte *screen, int16 x, int16 y);
+ /**
+ * This will just change a pixel directly on displayscreen. It is supposed to be
+ * only used on upscaled-Hires games where hires content needs to get drawn ONTO
+ * the upscaled display screen (like japanese fonts, hires portraits, etc.).
+ */
+ void putPixelOnDisplay(int16 x, int16 y, byte color) {
+ int offset = y * _displayWidth + x;
+ _displayScreen[offset] = color;
+ }
- void (GfxScreen::*_putPixelPtr) (int16 x, int16 y, byte drawMask, byte color, byte priority, byte control);
- void putPixelNormal (int16 x, int16 y, byte drawMask, byte color, byte priority, byte control);
- void putPixelDisplayUpscaled (int16 x, int16 y, byte drawMask, byte color, byte priority, byte control);
- void putPixelAllUpscaled (int16 x, int16 y, byte drawMask, byte color, byte priority, byte control);
+ // Upscales a pixel and puts it on display screen only
+ void putScaledPixelOnDisplay(int16 x, int16 y, byte color) {
+ int displayOffset = 0;
+
+ switch (_upscaledHires) {
+ case GFX_SCREEN_UPSCALED_640x400:
+ displayOffset = (y * 2) * _displayWidth + x * 2; // straight 1 pixel -> 2 mapping
+
+ _displayScreen[displayOffset] = color;
+ _displayScreen[displayOffset + 1] = color;
+ _displayScreen[displayOffset + _displayWidth] = color;
+ _displayScreen[displayOffset + _displayWidth + 1] = color;
+ break;
+
+ case GFX_SCREEN_UPSCALED_640x440: {
+ int16 startY = (y * 11) / 5;
+ int16 endY = ((y + 1) * 11) / 5;
+ displayOffset = (startY * _displayWidth) + x * 2;
+
+ for (int16 curY = startY; curY < endY; curY++) {
+ _displayScreen[displayOffset] = color;
+ _displayScreen[displayOffset + 1] = color;
+ displayOffset += _displayWidth;
+ }
+ break;
+ }
+ case GFX_SCREEN_UPSCALED_640x480: {
+ int16 startY = (y * 12) / 5;
+ int16 endY = ((y + 1) * 12) / 5;
+ displayOffset = (startY * _displayWidth) + x * 2;
+
+ for (int16 curY = startY; curY < endY; curY++) {
+ _displayScreen[displayOffset] = color;
+ _displayScreen[displayOffset + 1] = color;
+ displayOffset += _displayWidth;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
- byte (GfxScreen::*_getPixelPtr) (byte *screen, int16 x, int16 y);
- byte getPixelNormal (byte *screen, int16 x, int16 y);
- byte getPixelUpscaled (byte *screen, int16 x, int16 y);
+ /**
+ * This is used to put font pixels onto the screen - we adjust differently, so that we won't
+ * do triple pixel lines in any case on upscaled hires. That way the font will not get distorted
+ * Sierra SCI didn't do this
+ */
+ void putFontPixel(int16 startingY, int16 x, int16 y, byte color) {
+ int16 actualY = startingY + y;
+ if (_fontIsUpscaled) {
+ // Do not scale ourselves, but put it on the display directly
+ putPixelOnDisplay(x, actualY, color);
+ } else {
+ int offset = actualY * _width + x;
+
+ _visualScreen[offset] = color;
+ switch (_upscaledHires) {
+ case GFX_SCREEN_UPSCALED_DISABLED:
+ _displayScreen[offset] = color;
+ break;
+ case GFX_SCREEN_UPSCALED_640x400:
+ case GFX_SCREEN_UPSCALED_640x440:
+ case GFX_SCREEN_UPSCALED_640x480: {
+ // to 1-> 4 pixels upscaling for all of those, so that fonts won't look weird
+ int displayOffset = (_upscaledHeightMapping[startingY] + y * 2) * _displayWidth + x * 2;
+ _displayScreen[displayOffset] = color;
+ _displayScreen[displayOffset + 1] = color;
+ displayOffset += _displayWidth;
+ _displayScreen[displayOffset] = color;
+ _displayScreen[displayOffset + 1] = color;
+ break;
+ }
+ default:
+ putScaledPixelOnDisplay(x, actualY, color);
+ break;
+ }
+ }
+ }
- // pixel helper
- void putScaledPixelOnScreen(byte *screen, int16 x, int16 y, byte color);
+ byte getPixel(byte *screen, int16 x, int16 y) {
+ switch (_upscaledHires) {
+ case GFX_SCREEN_UPSCALED_480x300: {
+ int offset = ((y * 3) / 2) * _width + ((y * 3) / 2);
+
+ return screen[offset];
+ break;
+ }
+ default:
+ break;
+ }
+ return screen[y * _width + x];
+ }
+
+ byte getVisual(int16 x, int16 y) {
+ return getPixel(_visualScreen, x, y);
+ }
+ byte getPriority(int16 x, int16 y) {
+ return getPixel(_priorityScreen, x, y);
+ }
+ byte getControl(int16 x, int16 y) {
+ return getPixel(_controlScreen, x, y);
+ }
+
+ // Vector related public code - in here, so that it can be inlined
+ byte vectorGetPixel(byte *screen, int16 x, int16 y) {
+ return screen[y * _width + x];
+ }
+
+ byte vectorGetVisual(int16 x, int16 y) {
+ return vectorGetPixel(_visualScreen, x, y);
+ }
+ byte vectorGetPriority(int16 x, int16 y) {
+ return vectorGetPixel(_priorityScreen, x, y);
+ }
+ byte vectorGetControl(int16 x, int16 y) {
+ return vectorGetPixel(_controlScreen, x, y);
+ }
+
+ void vectorAdjustCoordinate(int16 *x, int16 *y) {
+ switch (_upscaledHires) {
+ case GFX_SCREEN_UPSCALED_480x300:
+ *x = (*x * 3) / 2;
+ *y = (*y * 3) / 2;
+ break;
+ default:
+ break;
+ }
+ }
};
} // End of namespace Sci
diff --git a/engines/sci/graphics/screen_item32.cpp b/engines/sci/graphics/screen_item32.cpp
new file mode 100644
index 0000000000..c3fdbb6845
--- /dev/null
+++ b/engines/sci/graphics/screen_item32.cpp
@@ -0,0 +1,647 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 "sci/console.h"
+#include "sci/resource.h"
+#include "sci/engine/kernel.h"
+#include "sci/engine/selector.h"
+#include "sci/engine/state.h"
+#include "sci/graphics/celobj32.h"
+#include "sci/graphics/frameout.h"
+#include "sci/graphics/screen_item32.h"
+#include "sci/graphics/view.h"
+
+namespace Sci {
+#pragma mark ScreenItem
+
+uint16 ScreenItem::_nextObjectId = 20000;
+
+ScreenItem::ScreenItem(const reg_t object) :
+_celObj(nullptr),
+_object(object),
+_pictureId(-1),
+_created(g_sci->_gfxFrameout->getScreenCount()),
+_updated(0),
+_deleted(0),
+_mirrorX(false) {
+ SegManager *segMan = g_sci->getEngineState()->_segMan;
+
+ setFromObject(segMan, object, true, true);
+ _plane = readSelector(segMan, object, SELECTOR(plane));
+}
+
+ScreenItem::ScreenItem(const reg_t plane, const CelInfo32 &celInfo) :
+_plane(plane),
+_useInsetRect(false),
+_z(0),
+_celInfo(celInfo),
+_celObj(nullptr),
+_fixPriority(false),
+_position(0, 0),
+_object(make_reg(0, _nextObjectId++)),
+_pictureId(-1),
+_created(g_sci->_gfxFrameout->getScreenCount()),
+_updated(0),
+_deleted(0),
+_mirrorX(false) {}
+
+ScreenItem::ScreenItem(const reg_t plane, const CelInfo32 &celInfo, const Common::Rect &rect) :
+_plane(plane),
+_useInsetRect(false),
+_z(0),
+_celInfo(celInfo),
+_celObj(nullptr),
+_fixPriority(false),
+_position(rect.left, rect.top),
+_object(make_reg(0, _nextObjectId++)),
+_pictureId(-1),
+_created(g_sci->_gfxFrameout->getScreenCount()),
+_updated(0),
+_deleted(0),
+_mirrorX(false) {
+ if (celInfo.type == kCelTypeColor) {
+ _insetRect = rect;
+ }
+}
+
+ScreenItem::ScreenItem(const reg_t plane, const CelInfo32 &celInfo, const Common::Point &position, const ScaleInfo &scaleInfo) :
+_plane(plane),
+_scale(scaleInfo),
+_useInsetRect(false),
+_z(0),
+_celInfo(celInfo),
+_celObj(nullptr),
+_fixPriority(false),
+_position(position),
+_object(make_reg(0, _nextObjectId++)),
+_pictureId(-1),
+_created(g_sci->_gfxFrameout->getScreenCount()),
+_updated(0),
+_deleted(0),
+_mirrorX(false) {}
+
+ScreenItem::ScreenItem(const ScreenItem &other) :
+_plane(other._plane),
+_scale(other._scale),
+_useInsetRect(other._useInsetRect),
+_celInfo(other._celInfo),
+_celObj(nullptr),
+_object(other._object),
+_mirrorX(other._mirrorX),
+_scaledPosition(other._scaledPosition),
+_screenRect(other._screenRect) {
+ if (other._useInsetRect) {
+ _insetRect = other._insetRect;
+ }
+}
+
+void ScreenItem::operator=(const ScreenItem &other) {
+ // NOTE: The original engine did not check for differences in `_celInfo`
+ // to clear `_celObj` here; instead, it unconditionally set `_celInfo`,
+ // didn't clear `_celObj`, and did hacky stuff in `kIsOnMe` to avoid
+ // testing a mismatched `_celObj`. See `GfxFrameout::kernelIsOnMe` for
+ // more detail.
+ if (_celInfo != other._celInfo) {
+ _celInfo = other._celInfo;
+ delete _celObj;
+ _celObj = nullptr;
+ }
+
+ _screenRect = other._screenRect;
+ _mirrorX = other._mirrorX;
+ _useInsetRect = other._useInsetRect;
+ if (other._useInsetRect) {
+ _insetRect = other._insetRect;
+ }
+ _scale = other._scale;
+ _scaledPosition = other._scaledPosition;
+}
+
+ScreenItem::~ScreenItem() {
+ delete _celObj;
+}
+
+void ScreenItem::init() {
+ _nextObjectId = 20000;
+}
+
+void ScreenItem::setFromObject(SegManager *segMan, const reg_t object, const bool updateCel, const bool updateBitmap) {
+ _position.x = readSelectorValue(segMan, object, SELECTOR(x));
+ _position.y = readSelectorValue(segMan, object, SELECTOR(y));
+ _scale.x = readSelectorValue(segMan, object, SELECTOR(scaleX));
+ _scale.y = readSelectorValue(segMan, object, SELECTOR(scaleY));
+ _scale.max = readSelectorValue(segMan, object, SELECTOR(maxScale));
+ _scale.signal = (ScaleSignals32)(readSelectorValue(segMan, object, SELECTOR(scaleSignal)) & 3);
+
+ if (updateCel) {
+ _celInfo.resourceId = (GuiResourceId)readSelectorValue(segMan, object, SELECTOR(view));
+ _celInfo.loopNo = readSelectorValue(segMan, object, SELECTOR(loop));
+ _celInfo.celNo = readSelectorValue(segMan, object, SELECTOR(cel));
+
+ if (_celInfo.resourceId <= kPlanePic) {
+ // TODO: Enhance GfxView or ResourceManager to allow
+ // metadata for resources to be retrieved once, from a
+ // single location
+ Resource *view = g_sci->getResMan()->findResource(ResourceId(kResourceTypeView, _celInfo.resourceId), false);
+ if (!view) {
+ error("Failed to load resource %d", _celInfo.resourceId);
+ }
+
+ // NOTE: +2 because the header size field itself is excluded from
+ // the header size in the data
+ const uint16 headerSize = READ_SCI11ENDIAN_UINT16(view->data) + 2;
+ const uint8 loopCount = view->data[2];
+ const uint8 loopSize = view->data[12];
+
+ if (_celInfo.loopNo >= loopCount) {
+ const int maxLoopNo = loopCount - 1;
+ _celInfo.loopNo = maxLoopNo;
+ writeSelectorValue(segMan, object, SELECTOR(loop), maxLoopNo);
+ }
+
+ byte *loopData = view->data + headerSize + (_celInfo.loopNo * loopSize);
+ const int8 seekEntry = loopData[0];
+ if (seekEntry != -1) {
+ loopData = view->data + headerSize + (seekEntry * loopSize);
+ }
+ const uint8 celCount = loopData[2];
+ if (_celInfo.celNo >= celCount) {
+ const int maxCelNo = celCount - 1;
+ _celInfo.celNo = maxCelNo;
+ writeSelectorValue(segMan, object, SELECTOR(cel), maxCelNo);
+ }
+ }
+ }
+
+ if (updateBitmap) {
+ const reg_t bitmap = readSelector(segMan, object, SELECTOR(bitmap));
+ if (!bitmap.isNull()) {
+ _celInfo.bitmap = bitmap;
+ _celInfo.type = kCelTypeMem;
+ } else {
+ _celInfo.bitmap = NULL_REG;
+ _celInfo.type = kCelTypeView;
+ }
+ }
+
+ if (updateCel || updateBitmap) {
+ delete _celObj;
+ _celObj = nullptr;
+ }
+
+ if (readSelectorValue(segMan, object, SELECTOR(fixPriority))) {
+ _fixPriority = true;
+ _priority = readSelectorValue(segMan, object, SELECTOR(priority));
+ } else {
+ _fixPriority = false;
+ writeSelectorValue(segMan, object, SELECTOR(priority), _position.y);
+ }
+
+ _z = readSelectorValue(segMan, object, SELECTOR(z));
+ _position.y -= _z;
+
+ if (readSelectorValue(segMan, object, SELECTOR(useInsetRect))) {
+ _useInsetRect = true;
+ _insetRect.left = readSelectorValue(segMan, object, SELECTOR(inLeft));
+ _insetRect.top = readSelectorValue(segMan, object, SELECTOR(inTop));
+ _insetRect.right = readSelectorValue(segMan, object, SELECTOR(inRight)) + 1;
+ _insetRect.bottom = readSelectorValue(segMan, object, SELECTOR(inBottom)) + 1;
+ } else {
+ _useInsetRect = false;
+ }
+
+ segMan->getObject(object)->clearInfoSelectorFlag(kInfoFlagViewVisible);
+}
+
+void ScreenItem::calcRects(const Plane &plane) {
+ const int16 scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth;
+ const int16 scriptHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight;
+ const int16 screenWidth = g_sci->_gfxFrameout->getCurrentBuffer().screenWidth;
+ const int16 screenHeight = g_sci->_gfxFrameout->getCurrentBuffer().screenHeight;
+
+ const CelObj &celObj = getCelObj();
+
+ Common::Rect celRect(celObj._width, celObj._height);
+ if (_useInsetRect) {
+ if (_insetRect.intersects(celRect)) {
+ _insetRect.clip(celRect);
+ } else {
+ _insetRect = Common::Rect();
+ }
+ } else {
+ _insetRect = celRect;
+ }
+
+ Ratio scaleX, scaleY;
+
+ if (_scale.signal & kScaleSignalDoScaling32) {
+ if (_scale.signal & kScaleSignalUseVanishingPoint) {
+ int num = _scale.max * (_position.y - plane._vanishingPoint.y) / (scriptWidth - plane._vanishingPoint.y);
+ scaleX = Ratio(num, 128);
+ scaleY = Ratio(num, 128);
+ } else {
+ scaleX = Ratio(_scale.x, 128);
+ scaleY = Ratio(_scale.y, 128);
+ }
+ }
+
+ if (scaleX.getNumerator() && scaleY.getNumerator()) {
+ _screenItemRect = _insetRect;
+
+ const Ratio celToScreenX(screenWidth, celObj._scaledWidth);
+ const Ratio celToScreenY(screenHeight, celObj._scaledHeight);
+
+ // Cel may use a coordinate system that is not the same size as the
+ // script coordinate system (usually this means high-resolution
+ // pictures with low-resolution scripts)
+ if (celObj._scaledWidth != scriptWidth || celObj._scaledHeight != scriptHeight) {
+ if (_useInsetRect) {
+ const Ratio scriptToCelX(celObj._scaledWidth, scriptWidth);
+ const Ratio scriptToCelY(celObj._scaledHeight, scriptHeight);
+ mulru(_screenItemRect, scriptToCelX, scriptToCelY, 0);
+
+ if (_screenItemRect.intersects(celRect)) {
+ _screenItemRect.clip(celRect);
+ } else {
+ _screenItemRect = Common::Rect();
+ }
+ }
+
+ int displaceX = celObj._displace.x;
+ int displaceY = celObj._displace.y;
+
+ if (_mirrorX != celObj._mirrorX && _celInfo.type != kCelTypePic) {
+ displaceX = celObj._width - celObj._displace.x - 1;
+ }
+
+ if (!scaleX.isOne() || !scaleY.isOne()) {
+ mulinc(_screenItemRect, scaleX, scaleY);
+ displaceX = (displaceX * scaleX).toInt();
+ displaceY = (displaceY * scaleY).toInt();
+ }
+
+ mulinc(_screenItemRect, celToScreenX, celToScreenY);
+ displaceX = (displaceX * celToScreenX).toInt();
+ displaceY = (displaceY * celToScreenY).toInt();
+
+ const Ratio scriptToScreenX = Ratio(screenWidth, scriptWidth);
+ const Ratio scriptToScreenY = Ratio(screenHeight, scriptHeight);
+
+ if (/* TODO: dword_C6288 */ false && _celInfo.type == kCelTypePic) {
+ _scaledPosition.x = _position.x;
+ _scaledPosition.y = _position.y;
+ } else {
+ _scaledPosition.x = (_position.x * scriptToScreenX).toInt() - displaceX;
+ _scaledPosition.y = (_position.y * scriptToScreenY).toInt() - displaceY;
+ }
+
+ _screenItemRect.translate(_scaledPosition.x, _scaledPosition.y);
+
+ if (_mirrorX != celObj._mirrorX && _celInfo.type == kCelTypePic) {
+ Common::Rect temp(_insetRect);
+
+ if (!scaleX.isOne()) {
+ mulinc(temp, scaleX, Ratio());
+ }
+
+ mulinc(temp, celToScreenX, Ratio());
+
+ CelObjPic *celObjPic = dynamic_cast<CelObjPic *>(_celObj);
+ temp.translate((celObjPic->_relativePosition.x * scriptToScreenX).toInt() - displaceX, 0);
+
+ // TODO: This is weird.
+ int deltaX = plane._planeRect.width() - temp.right - 1 - temp.left;
+
+ _scaledPosition.x += deltaX;
+ _screenItemRect.translate(deltaX, 0);
+ }
+
+ _scaledPosition.x += plane._planeRect.left;
+ _scaledPosition.y += plane._planeRect.top;
+ _screenItemRect.translate(plane._planeRect.left, plane._planeRect.top);
+
+ _ratioX = scaleX * celToScreenX;
+ _ratioY = scaleY * celToScreenY;
+ } else {
+ int displaceX = celObj._displace.x;
+ if (_mirrorX != celObj._mirrorX && _celInfo.type != kCelTypePic) {
+ displaceX = celObj._width - celObj._displace.x - 1;
+ }
+
+ if (!scaleX.isOne() || !scaleY.isOne()) {
+ mulinc(_screenItemRect, scaleX, scaleY);
+ // TODO: This was in the original code, baked into the
+ // multiplication though it is not immediately clear
+ // why this is the only one that reduces the BR corner
+ _screenItemRect.right -= 1;
+ _screenItemRect.bottom -= 1;
+ }
+
+ _scaledPosition.x = _position.x - (displaceX * scaleX).toInt();
+ _scaledPosition.y = _position.y - (celObj._displace.y * scaleY).toInt();
+ _screenItemRect.translate(_scaledPosition.x, _scaledPosition.y);
+
+ if (_mirrorX != celObj._mirrorX && _celInfo.type == kCelTypePic) {
+ Common::Rect temp(_insetRect);
+
+ if (!scaleX.isOne()) {
+ mulinc(temp, scaleX, Ratio());
+ temp.right -= 1;
+ }
+
+ CelObjPic *celObjPic = dynamic_cast<CelObjPic *>(_celObj);
+ temp.translate(celObjPic->_relativePosition.x - (displaceX * scaleX).toInt(), celObjPic->_relativePosition.y - (celObj._displace.y * scaleY).toInt());
+
+ // TODO: This is weird.
+ int deltaX = plane._gameRect.width() - temp.right - 1 - temp.left;
+
+ _scaledPosition.x += deltaX;
+ _screenItemRect.translate(deltaX, 0);
+ }
+
+ _scaledPosition.x += plane._gameRect.left;
+ _scaledPosition.y += plane._gameRect.top;
+ _screenItemRect.translate(plane._gameRect.left, plane._gameRect.top);
+
+ if (celObj._scaledWidth != screenWidth || celObj._scaledHeight != screenHeight) {
+ mulru(_scaledPosition, celToScreenX, celToScreenY);
+ mulru(_screenItemRect, celToScreenX, celToScreenY, 1);
+ }
+
+ _ratioX = scaleX * celToScreenX;
+ _ratioY = scaleY * celToScreenY;
+ }
+
+ _screenRect = _screenItemRect;
+
+ if (_screenRect.intersects(plane._screenRect)) {
+ _screenRect.clip(plane._screenRect);
+ } else {
+ _screenRect.right = 0;
+ _screenRect.bottom = 0;
+ _screenRect.left = 0;
+ _screenRect.top = 0;
+ }
+
+ if (!_fixPriority) {
+ _priority = _z + _position.y;
+ }
+ } else {
+ _screenRect.left = 0;
+ _screenRect.top = 0;
+ _screenRect.right = 0;
+ _screenRect.bottom = 0;
+ }
+}
+
+CelObj &ScreenItem::getCelObj() const {
+ if (_celObj == nullptr) {
+ switch (_celInfo.type) {
+ case kCelTypeView:
+ _celObj = new CelObjView(_celInfo.resourceId, _celInfo.loopNo, _celInfo.celNo);
+ break;
+ case kCelTypePic:
+ error("Internal error, pic screen item with no cel.");
+ break;
+ case kCelTypeMem:
+ _celObj = new CelObjMem(_celInfo.bitmap);
+ break;
+ case kCelTypeColor:
+ _celObj = new CelObjColor(_celInfo.color, _insetRect.width(), _insetRect.height());
+ break;
+ }
+ }
+
+ return *_celObj;
+}
+
+void ScreenItem::printDebugInfo(Console *con) const {
+ con->debugPrintf("%04x:%04x (%s), prio %d, x %d, y %d, z: %d, scaledX: %d, scaledY: %d flags: %d\n",
+ _object.getSegment(), _object.getOffset(),
+ g_sci->getEngineState()->_segMan->getObjectName(_object),
+ _priority,
+ _position.x,
+ _position.y,
+ _z,
+ _scaledPosition.x,
+ _scaledPosition.y,
+ _created | (_updated << 1) | (_deleted << 2)
+ );
+ con->debugPrintf(" screen rect (%d, %d, %d, %d)\n", PRINT_RECT(_screenRect));
+ if (_useInsetRect) {
+ con->debugPrintf(" inset rect: (%d, %d, %d, %d)\n", PRINT_RECT(_insetRect));
+ }
+
+ Common::String celType;
+ switch (_celInfo.type) {
+ case kCelTypePic:
+ celType = "pic";
+ break;
+ case kCelTypeView:
+ celType = "view";
+ break;
+ case kCelTypeColor:
+ celType = "color";
+ break;
+ case kCelTypeMem:
+ celType = "mem";
+ break;
+ }
+
+ con->debugPrintf(" type: %s, res %d, loop %d, cel %d, bitmap %04x:%04x, color: %d\n",
+ celType.c_str(),
+ _celInfo.resourceId,
+ _celInfo.loopNo,
+ _celInfo.celNo,
+ PRINT_REG(_celInfo.bitmap),
+ _celInfo.color
+ );
+ if (_celObj != nullptr) {
+ con->debugPrintf(" width %d, height %d, scaledWidth %d, scaledHeight %d\n",
+ _celObj->_width,
+ _celObj->_height,
+ _celObj->_scaledWidth,
+ _celObj->_scaledHeight
+ );
+ }
+}
+
+void ScreenItem::update(const reg_t object) {
+ SegManager *segMan = g_sci->getEngineState()->_segMan;
+
+ const GuiResourceId view = readSelectorValue(segMan, object, SELECTOR(view));
+ const int16 loopNo = readSelectorValue(segMan, object, SELECTOR(loop));
+ const int16 celNo = readSelectorValue(segMan, object, SELECTOR(cel));
+
+ const bool updateCel = (
+ _celInfo.resourceId != view ||
+ _celInfo.loopNo != loopNo ||
+ _celInfo.celNo != celNo
+ );
+
+ const bool updateBitmap = !readSelector(segMan, object, SELECTOR(bitmap)).isNull();
+
+ setFromObject(segMan, object, updateCel, updateBitmap);
+
+ if (!_created) {
+ _updated = g_sci->_gfxFrameout->getScreenCount();
+ }
+
+ _deleted = 0;
+}
+
+// TODO: This code is quite similar to calcRects, so try to deduplicate
+// if possible
+Common::Rect ScreenItem::getNowSeenRect(const Plane &plane) const {
+ CelObj &celObj = getCelObj();
+
+ Common::Rect celObjRect(celObj._width, celObj._height);
+ Common::Rect nsRect;
+
+ if (_useInsetRect) {
+ // TODO: This is weird. Checking to see if the inset rect is
+ // fully inside the bounds of the celObjRect, and then
+ // clipping to the celObjRect, is pretty useless.
+ if (_insetRect.right > 0 && _insetRect.bottom > 0 && _insetRect.left < celObj._width && _insetRect.top < celObj._height) {
+ nsRect = _insetRect;
+ nsRect.clip(celObjRect);
+ } else {
+ nsRect = Common::Rect();
+ }
+ } else {
+ nsRect = celObjRect;
+ }
+
+ const uint16 scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth;
+ const uint16 scriptHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight;
+
+ Ratio scaleX, scaleY;
+ if (_scale.signal & kScaleSignalDoScaling32) {
+ if (_scale.signal & kScaleSignalUseVanishingPoint) {
+ int num = _scale.max * (_position.y - plane._vanishingPoint.y) / (scriptWidth - plane._vanishingPoint.y);
+ scaleX = Ratio(num, 128);
+ scaleY = Ratio(num, 128);
+ } else {
+ scaleX = Ratio(_scale.x, 128);
+ scaleY = Ratio(_scale.y, 128);
+ }
+ }
+
+ if (scaleX.getNumerator() == 0 || scaleY.getNumerator() == 0) {
+ return Common::Rect();
+ }
+
+ int16 displaceX = celObj._displace.x;
+ int16 displaceY = celObj._displace.y;
+
+ if (_mirrorX != celObj._mirrorX && _celInfo.type != kCelTypePic) {
+ displaceX = celObj._width - displaceX - 1;
+ }
+
+ if (celObj._scaledWidth != scriptWidth || celObj._scaledHeight != scriptHeight) {
+ if (_useInsetRect) {
+ Ratio scriptToCelX(celObj._scaledWidth, scriptWidth);
+ Ratio scriptToCelY(celObj._scaledHeight, scriptHeight);
+ mulru(nsRect, scriptToCelX, scriptToCelY, 0);
+
+ // TODO: This is weird. Checking to see if the inset rect is
+ // fully inside the bounds of the celObjRect, and then
+ // clipping to the celObjRect, is pretty useless.
+ if (nsRect.right > 0 && nsRect.bottom > 0 && nsRect.left < celObj._width && nsRect.top < celObj._height) {
+ nsRect.clip(celObjRect);
+ } else {
+ nsRect = Common::Rect();
+ }
+ }
+
+ if (!scaleX.isOne() || !scaleY.isOne()) {
+ mulinc(nsRect, scaleX, scaleY);
+ // TODO: This was in the original code, baked into the
+ // multiplication though it is not immediately clear
+ // why this is the only one that reduces the BR corner
+ nsRect.right -= 1;
+ nsRect.bottom -= 1;
+ }
+
+ Ratio celToScriptX(scriptWidth, celObj._scaledWidth);
+ Ratio celToScriptY(scriptHeight, celObj._scaledHeight);
+
+ displaceX = (displaceX * scaleX * celToScriptX).toInt();
+ displaceY = (displaceY * scaleY * celToScriptY).toInt();
+
+ mulinc(nsRect, celToScriptX, celToScriptY);
+ nsRect.translate(_position.x - displaceX, _position.y - displaceY);
+ } else {
+ if (!scaleX.isOne() || !scaleY.isOne()) {
+ mulinc(nsRect, scaleX, scaleY);
+ // TODO: This was in the original code, baked into the
+ // multiplication though it is not immediately clear
+ // why this is the only one that reduces the BR corner
+ nsRect.right -= 1;
+ nsRect.bottom -= 1;
+ }
+
+ displaceX = (displaceX * scaleX).toInt();
+ displaceY = (displaceY * scaleY).toInt();
+ nsRect.translate(_position.x - displaceX, _position.y - displaceY);
+
+ if (_mirrorX != celObj._mirrorX && _celInfo.type != kCelTypePic) {
+ nsRect.translate(plane._gameRect.width() - nsRect.width(), 0);
+ }
+ }
+
+ return nsRect;
+}
+
+#pragma mark -
+#pragma mark ScreenItemList
+ScreenItem *ScreenItemList::findByObject(const reg_t object) const {
+ const_iterator screenItemIt = Common::find_if(begin(), end(), FindByObject<ScreenItem *>(object));
+
+ if (screenItemIt == end()) {
+ return nullptr;
+ }
+
+ return *screenItemIt;
+}
+void ScreenItemList::sort() {
+ // TODO: SCI engine used _unsorted as an array of indexes into the
+ // list itself and then performed the same swap operations on the
+ // _unsorted array as the _storage array during sorting, but the
+ // only reason to do this would be if some of the pointers in the
+ // list were replaced so the pointer values themselves couldn’t
+ // simply be recorded and then restored later. It is not yet
+ // verified whether this simplification of the sort/unsort is
+ // safe.
+ for (size_type i = 0; i < size(); ++i) {
+ _unsorted[i] = (*this)[i];
+ }
+
+ Common::sort(begin(), end(), sortHelper);
+}
+void ScreenItemList::unsort() {
+ for (size_type i = 0; i < size(); ++i) {
+ (*this)[i] = _unsorted[i];
+ }
+}
+
+} // End of namespace Sci
diff --git a/engines/sci/graphics/screen_item32.h b/engines/sci/graphics/screen_item32.h
new file mode 100644
index 0000000000..977d80ebad
--- /dev/null
+++ b/engines/sci/graphics/screen_item32.h
@@ -0,0 +1,288 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 SCI_GRAPHICS_SCREEN_ITEM32_H
+#define SCI_GRAPHICS_SCREEN_ITEM32_H
+
+#include "common/rect.h"
+#include "sci/graphics/celobj32.h"
+#include "sci/graphics/lists32.h"
+
+namespace Sci {
+
+enum ScaleSignals32 {
+ kScaleSignalNone = 0,
+ kScaleSignalDoScaling32 = 1, // enables scaling when drawing that cel (involves scaleX and scaleY)
+ kScaleSignalUseVanishingPoint = 2,
+ // TODO: Is this actually a thing? I have not seen it and
+ // the original engine masks &3 where it uses scale signals.
+ kScaleSignalDisableGlobalScaling32 = 4
+};
+
+struct ScaleInfo {
+ int x, y, max;
+ ScaleSignals32 signal;
+ ScaleInfo() : x(128), y(128), max(100), signal(kScaleSignalNone) {}
+};
+
+class CelObj;
+class Plane;
+class SegManager;
+
+#pragma mark -
+#pragma mark ScreenItem
+
+/**
+ * A ScreenItem is the engine-side representation of a
+ * game script View.
+ */
+class ScreenItem {
+private:
+ /**
+ * A serial used for screen items that are generated
+ * inside the graphics engine, rather than the
+ * interpreter.
+ */
+ static uint16 _nextObjectId;
+
+ /**
+ * The parent plane of this screen item.
+ */
+ reg_t _plane;
+
+public:
+ /**
+ * Scaling data used to calculate the final screen
+ * dimensions of the screen item as well as the scaling
+ * ratios used when drawing the item to screen.
+ */
+ ScaleInfo _scale;
+
+private:
+ /**
+ * The position & dimensions of the screen item in
+ * screen coordinates. This rect includes the offset
+ * of the parent plane, but is not clipped to the
+ * screen, so may include coordinates that are
+ * offscreen.
+ */
+ Common::Rect _screenItemRect;
+
+ /**
+ * TODO: Document
+ */
+ bool _useInsetRect;
+
+ /**
+ * TODO: Documentation
+ * The insetRect is also used to describe the fill
+ * rectangle of a screen item that is drawn using
+ * CelObjColor.
+ */
+ Common::Rect _insetRect;
+
+ /**
+ * The z-index of the screen item in pseudo-3D space.
+ * Higher values are drawn on top of lower values.
+ */
+ int _z;
+
+ /**
+ * Sets the common properties of a screen item that must
+ * be set both during creation and update of a screen
+ * item.
+ */
+ void setFromObject(SegManager *segMan, const reg_t object, const bool updateCel, const bool updateBitmap);
+
+public:
+ /**
+ * A descriptor for the cel object represented by the
+ * screen item.
+ */
+ CelInfo32 _celInfo;
+
+ /**
+ * The cel object used to actually render the screen
+ * item. This member is populated by calling
+ * `getCelObj`.
+ */
+ mutable CelObj *_celObj;
+
+ /**
+ * If set, the priority for this screen item is fixed
+ * in place. Otherwise, the priority of the screen item
+ * is calculated from its y-position + z-index.
+ */
+ bool _fixPriority;
+
+ /**
+ * The rendering priority of the screen item, relative
+ * only to the other screen items within the same plane.
+ * Higher priorities are drawn above lower priorities.
+ */
+ int16 _priority;
+
+ /**
+ * The top-left corner of the screen item, in game
+ * script coordinates, relative to the parent plane.
+ */
+ Common::Point _position;
+
+ /**
+ * The associated View script object that was
+ * used to create the ScreenItem, or a numeric
+ * value in the case of a ScreenItem that was
+ * generated outside of the VM.
+ */
+ reg_t _object;
+
+ /**
+ * For screen items representing picture resources,
+ * the resource ID of the picture.
+ */
+ GuiResourceId _pictureId;
+
+ /**
+ * Flags indicating the state of the screen item.
+ * - `created` is set when the screen item is first
+ * created, either from a VM object or from within the
+ * engine itself
+ * - `updated` is set when `created` is not already set
+ * and the screen item is updated from a VM object
+ * - `deleted` is set by the parent plane, if the parent
+ * plane is a pic type and its picture resource ID has
+ * changed
+ */
+ int _created, _updated, _deleted; // ?
+
+ /**
+ * For screen items that represent picture cels, this
+ * value is set to match the `_mirrorX` property of the
+ * parent plane and indicates that the cel should be
+ * drawn horizontally mirrored. For final drawing, it is
+ * XORed with the `_mirrorX` property of the cel object.
+ * The cel object's `_mirrorX` property comes from the
+ * resource data itself.
+ */
+ bool _mirrorX;
+
+ /**
+ * The scaling ratios to use when drawing this screen
+ * item. These values are calculated according to the
+ * scale info whenever the screen item is updated.
+ */
+ Ratio _ratioX, _ratioY;
+
+ /**
+ * The top-left corner of the screen item, in screen
+ * coordinates.
+ */
+ Common::Point _scaledPosition;
+
+ /**
+ * The position & dimensions of the screen item in
+ * screen coordinates. This rect includes the offset of
+ * the parent plane and is clipped to the screen.
+ */
+ Common::Rect _screenRect;
+
+ /**
+ * Initialises static Plane members.
+ */
+ static void init();
+
+ ScreenItem(const reg_t screenItem);
+ ScreenItem(const reg_t plane, const CelInfo32 &celInfo);
+ ScreenItem(const reg_t plane, const CelInfo32 &celInfo, const Common::Rect &rect);
+ ScreenItem(const reg_t plane, const CelInfo32 &celInfo, const Common::Point &position, const ScaleInfo &scaleInfo);
+ ScreenItem(const ScreenItem &other);
+ ~ScreenItem();
+ void operator=(const ScreenItem &);
+
+ inline bool operator<(const ScreenItem &other) const {
+ if (_priority < other._priority) {
+ return true;
+ }
+
+ if (_priority == other._priority) {
+ if (_position.y + _z < other._position.y + other._z) {
+ return true;
+ }
+
+ if (_position.y + _z == other._position.y + other._z) {
+ return _object < other._object;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Calculates the dimensions and scaling parameters for
+ * the screen item, using the given plane as the parent
+ * plane for screen rect positioning.
+ *
+ * @note This method was called Update in SCI engine.
+ */
+ void calcRects(const Plane &plane);
+
+ /**
+ * Retrieves the corresponding cel object for this
+ * screen item. If a cel object does not already exist,
+ * one will be created and assigned.
+ */
+ CelObj &getCelObj() const;
+
+ void printDebugInfo(Console *con) const;
+
+ /**
+ * Updates the properties of the screen item from a
+ * VM object.
+ */
+ void update(const reg_t object);
+
+ /**
+ * Gets the "now seen" rect for the screen item, which
+ * represents the current size and position of the
+ * screen item on the screen in script coordinates.
+ */
+ Common::Rect getNowSeenRect(const Plane &plane) const;
+};
+
+#pragma mark -
+#pragma mark ScreenItemList
+
+typedef StablePointerArray<ScreenItem, 250> ScreenItemListBase;
+class ScreenItemList : public ScreenItemListBase {
+ static bool inline sortHelper(const ScreenItem *a, const ScreenItem *b) {
+ return *a < *b;
+ }
+public:
+ ScreenItem *_unsorted[250];
+
+ ScreenItem *findByObject(const reg_t object) const;
+ void sort();
+ void unsort();
+};
+} // End of namespace Sci
+
+#endif
diff --git a/engines/sci/graphics/text32.cpp b/engines/sci/graphics/text32.cpp
index 56ce73e8fa..99ffc6e328 100644
--- a/engines/sci/graphics/text32.cpp
+++ b/engines/sci/graphics/text32.cpp
@@ -29,363 +29,629 @@
#include "sci/engine/selector.h"
#include "sci/engine/state.h"
#include "sci/graphics/cache.h"
+#include "sci/graphics/celobj32.h"
#include "sci/graphics/compare.h"
#include "sci/graphics/font.h"
+#include "sci/graphics/frameout.h"
#include "sci/graphics/screen.h"
#include "sci/graphics/text32.h"
namespace Sci {
-#define BITMAP_HEADER_SIZE 46
+int16 GfxText32::_defaultFontId = 0;
+
+GfxText32::GfxText32(SegManager *segMan, GfxCache *fonts) :
+ _segMan(segMan),
+ _cache(fonts),
+ _scaledWidth(g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth),
+ _scaledHeight(g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight),
+ // Not a typo, the original engine did not initialise height, only width
+ _width(0),
+ _text(""),
+ _field_20(0),
+ _field_2C(2),
+ _field_30(0),
+ _field_34(0),
+ _field_38(0),
+ _field_3C(0),
+ _bitmap(NULL_REG) {
+ _fontId = _defaultFontId;
+ _font = _cache->getFont(_defaultFontId);
+ }
-#define SCI_TEXT32_ALIGNMENT_RIGHT -1
-#define SCI_TEXT32_ALIGNMENT_CENTER 1
-#define SCI_TEXT32_ALIGNMENT_LEFT 0
+reg_t GfxText32::createFontBitmap(int16 width, int16 height, const Common::Rect &rect, const Common::String &text, const uint8 foreColor, const uint8 backColor, const uint8 skipColor, const GuiResourceId fontId, const TextAlign alignment, const int16 borderColor, const bool dimmed, const bool doScaling) {
-GfxText32::GfxText32(SegManager *segMan, GfxCache *fonts, GfxScreen *screen)
- : _segMan(segMan), _cache(fonts), _screen(screen) {
-}
+ _field_22 = 0;
+ _borderColor = borderColor;
+ _text = text;
+ _textRect = rect;
+ _width = width;
+ _height = height;
+ _foreColor = foreColor;
+ _backColor = backColor;
+ _skipColor = skipColor;
+ _alignment = alignment;
+ _dimmed = dimmed;
-GfxText32::~GfxText32() {
-}
+ setFont(fontId);
-reg_t GfxText32::createScrollTextBitmap(Common::String text, reg_t textObject, uint16 maxWidth, uint16 maxHeight, reg_t prevHunk) {
- return createTextBitmapInternal(text, textObject, maxWidth, maxHeight, prevHunk);
+ if (doScaling) {
+ int16 scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth;
+ int16 scriptHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight;
-}
-reg_t GfxText32::createTextBitmap(reg_t textObject, uint16 maxWidth, uint16 maxHeight, reg_t prevHunk) {
- reg_t stringObject = readSelector(_segMan, textObject, SELECTOR(text));
- // The object in the text selector of the item can be either a raw string
- // or a Str object. In the latter case, we need to access the object's data
- // selector to get the raw string.
- if (_segMan->isHeapObject(stringObject))
- stringObject = readSelector(_segMan, stringObject, SELECTOR(data));
+ Ratio scaleX(_scaledWidth, scriptWidth);
+ Ratio scaleY(_scaledHeight, scriptHeight);
- Common::String text = _segMan->getString(stringObject);
+ _width = (_width * scaleX).toInt();
+ _height = (_height * scaleY).toInt();
+ mulinc(_textRect, scaleX, scaleY);
+ }
- return createTextBitmapInternal(text, textObject, maxWidth, maxHeight, prevHunk);
-}
+ // _textRect represents where text is drawn inside the
+ // bitmap; clipRect is the entire bitmap
+ Common::Rect bitmapRect(_width, _height);
-reg_t GfxText32::createTextBitmapInternal(Common::String &text, reg_t textObject, uint16 maxWidth, uint16 maxHeight, reg_t prevHunk) {
- // HACK: The character offsets of the up and down arrow buttons are off by one
- // in GK1, for some unknown reason. Fix them here.
- if (text.size() == 1 && (text[0] == 29 || text[0] == 30)) {
- text.setChar(text[0] + 1, 0);
- }
- GuiResourceId fontId = readSelectorValue(_segMan, textObject, SELECTOR(font));
- GfxFont *font = _cache->getFont(fontId);
- bool dimmed = readSelectorValue(_segMan, textObject, SELECTOR(dimmed));
- int16 alignment = readSelectorValue(_segMan, textObject, SELECTOR(mode));
- uint16 foreColor = readSelectorValue(_segMan, textObject, SELECTOR(fore));
- uint16 backColor = readSelectorValue(_segMan, textObject, SELECTOR(back));
-
- Common::Rect nsRect = g_sci->_gfxCompare->getNSRect(textObject);
- uint16 width = nsRect.width() + 1;
- uint16 height = nsRect.height() + 1;
-
- // Limit rectangle dimensions, if requested
- if (maxWidth > 0)
- width = maxWidth;
- if (maxHeight > 0)
- height = maxHeight;
-
- // Upscale the coordinates/width if the fonts are already upscaled
- if (_screen->fontIsUpscaled()) {
- width = width * _screen->getDisplayWidth() / _screen->getWidth();
- height = height * _screen->getDisplayHeight() / _screen->getHeight();
+ if (_textRect.intersects(bitmapRect)) {
+ _textRect.clip(bitmapRect);
+ } else {
+ _textRect = Common::Rect();
}
- int entrySize = width * height + BITMAP_HEADER_SIZE;
- reg_t memoryId = NULL_REG;
- if (prevHunk.isNull()) {
- memoryId = _segMan->allocateHunkEntry("TextBitmap()", entrySize);
+ BitmapResource bitmap(_segMan, _width, _height, _skipColor, 0, 0, _scaledWidth, _scaledHeight, 0, false);
+ _bitmap = bitmap.getObject();
- // Scroll text objects have no bitmap selector!
- ObjVarRef varp;
- if (lookupSelector(_segMan, textObject, SELECTOR(bitmap), &varp, NULL) == kSelectorVariable)
- writeSelector(_segMan, textObject, SELECTOR(bitmap), memoryId);
- } else {
- memoryId = prevHunk;
+ erase(bitmapRect, false);
+
+ if (_borderColor > -1) {
+ drawFrame(bitmapRect, 1, _borderColor, false);
}
- byte *memoryPtr = _segMan->getHunkPointer(memoryId);
- if (prevHunk.isNull())
- memset(memoryPtr, 0, BITMAP_HEADER_SIZE);
+ drawTextBox();
+ return _bitmap;
+}
- byte *bitmap = memoryPtr + BITMAP_HEADER_SIZE;
- memset(bitmap, backColor, width * height);
+reg_t GfxText32::createFontBitmap(const CelInfo32 &celInfo, const Common::Rect &rect, const Common::String &text, const int16 foreColor, const int16 backColor, const GuiResourceId fontId, const int16 skipColor, const int16 borderColor, const bool dimmed) {
+ _field_22 = 0;
+ _borderColor = borderColor;
+ _text = text;
+ _textRect = rect;
+ _foreColor = foreColor;
+ _dimmed = dimmed;
- // Save totalWidth, totalHeight
- WRITE_LE_UINT16(memoryPtr, width);
- WRITE_LE_UINT16(memoryPtr + 2, height);
+ setFont(fontId);
- int16 charCount = 0;
- uint16 curX = 0, curY = 0;
- const char *txt = text.c_str();
- int16 textWidth, textHeight, totalHeight = 0, offsetX = 0, offsetY = 0;
- uint16 start = 0;
+ int16 scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth;
+ int16 scriptHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight;
- // Calculate total text height
- while (*txt) {
- charCount = GetLongest(txt, width, font);
- if (charCount == 0)
- break;
+ int borderSize = 1;
+ mulinc(_textRect, Ratio(_scaledWidth, scriptWidth), Ratio(_scaledHeight, scriptHeight));
- Width(txt, 0, (int16)strlen(txt), fontId, textWidth, textHeight, true);
+ CelObjView view(celInfo.resourceId, celInfo.loopNo, celInfo.celNo);
+ _skipColor = view._transparentColor;
+ _width = view._width * _scaledWidth / view._scaledWidth;
+ _height = view._height * _scaledHeight / view._scaledHeight;
- totalHeight += textHeight;
- txt += charCount;
- while (*txt == ' ')
- txt++; // skip over breaking spaces
+ Common::Rect bitmapRect(_width, _height);
+ if (_textRect.intersects(bitmapRect)) {
+ _textRect.clip(bitmapRect);
+ } else {
+ _textRect = Common::Rect();
}
- txt = text.c_str();
-
- // Draw text in buffer
- while (*txt) {
- charCount = GetLongest(txt, width, font);
- if (charCount == 0)
- break;
- Width(txt, start, charCount, fontId, textWidth, textHeight, true);
-
- switch (alignment) {
- case SCI_TEXT32_ALIGNMENT_RIGHT:
- offsetX = width - textWidth;
- break;
- case SCI_TEXT32_ALIGNMENT_CENTER:
- // Center text both horizontally and vertically
- offsetX = (width - textWidth) / 2;
- offsetY = (height - totalHeight) / 2;
- break;
- case SCI_TEXT32_ALIGNMENT_LEFT:
- offsetX = 0;
- break;
-
- default:
- warning("Invalid alignment %d used in TextBox()", alignment);
- }
+ BitmapResource bitmap(_segMan, _width, _height, _skipColor, 0, 0, _scaledWidth, _scaledHeight, 0, false);
+ _bitmap = bitmap.getObject();
+ Buffer buffer(_width, _height, bitmap.getPixels());
+
+ // NOTE: The engine filled the bitmap pixels with 11 here, which is silly
+ // because then it just erased the bitmap using the skip color. So we don't
+ // fill the bitmap redundantly here.
- byte curChar;
-
- for (int i = 0; i < charCount; i++) {
- curChar = txt[i];
-
- switch (curChar) {
- case 0x0A:
- case 0x0D:
- case 0:
- break;
- case 0x7C:
- warning("Code processing isn't implemented in SCI32");
- break;
- default:
- font->drawToBuffer(curChar, curY + offsetY, curX + offsetX, foreColor, dimmed, bitmap, width, height);
- curX += font->getCharWidth(curChar);
- break;
+ _backColor = _skipColor;
+ erase(bitmapRect, false);
+ _backColor = backColor;
+
+ view.draw(buffer, bitmapRect, Common::Point(0, 0), false, Ratio(_scaledWidth, view._scaledWidth), Ratio(_scaledHeight, view._scaledHeight));
+
+ if (_backColor != skipColor && _foreColor != skipColor) {
+ erase(_textRect, false);
+ }
+
+ if (text.size() > 0) {
+ if (_foreColor == skipColor) {
+ error("TODO: Implement transparent text");
+ } else {
+ if (borderColor != -1) {
+ drawFrame(bitmapRect, borderSize, _borderColor, false);
}
- }
- curX = 0;
- curY += font->getHeight();
- txt += charCount;
- while (*txt == ' ')
- txt++; // skip over breaking spaces
+ drawTextBox();
+ }
}
- return memoryId;
+ return _bitmap;
}
-void GfxText32::disposeTextBitmap(reg_t hunkId) {
- _segMan->freeHunkEntry(hunkId);
+reg_t GfxText32::createTitledBitmap(const int16 width, const int16 height, const Common::Rect &textRect, const Common::String &text, const int16 foreColor, const int16 backColor, const int16 skipColor, const GuiResourceId fontId, const TextAlign alignment, const int16 borderColor, Common::String &title, const int16 titleForeColor, const int16 titleBackColor, const GuiResourceId titleFontId, const bool doScaling) {
+ warning("TODO: createTitledBitmap incomplete !");
+ return createFontBitmap(width, height, textRect, text, foreColor, backColor, skipColor, fontId, alignment, borderColor, false, doScaling);
}
-void GfxText32::drawTextBitmap(int16 x, int16 y, Common::Rect planeRect, reg_t textObject) {
- reg_t hunkId = readSelector(_segMan, textObject, SELECTOR(bitmap));
- drawTextBitmapInternal(x, y, planeRect, textObject, hunkId);
+void GfxText32::setFont(const GuiResourceId fontId) {
+ // NOTE: In SCI engine this calls FontMgr::BuildFontTable and then a font
+ // table is built on the FontMgr directly; instead, because we already have
+ // font resources, this code just grabs a font out of GfxCache.
+ if (fontId != _fontId) {
+ _fontId = fontId == -1 ? _defaultFontId : fontId;
+ _font = _cache->getFont(_fontId);
+ }
}
-void GfxText32::drawScrollTextBitmap(reg_t textObject, reg_t hunkId, uint16 x, uint16 y) {
- /*reg_t plane = readSelector(_segMan, textObject, SELECTOR(plane));
- Common::Rect planeRect;
- planeRect.top = readSelectorValue(_segMan, plane, SELECTOR(top));
- planeRect.left = readSelectorValue(_segMan, plane, SELECTOR(left));
- planeRect.bottom = readSelectorValue(_segMan, plane, SELECTOR(bottom));
- planeRect.right = readSelectorValue(_segMan, plane, SELECTOR(right));
+void GfxText32::drawFrame(const Common::Rect &rect, const int16 size, const uint8 color, const bool doScaling) {
+ Common::Rect targetRect = doScaling ? scaleRect(rect) : rect;
- drawTextBitmapInternal(x, y, planeRect, textObject, hunkId);*/
+ byte *bitmap = _segMan->getHunkPointer(_bitmap);
+ byte *pixels = bitmap + READ_SCI11ENDIAN_UINT32(bitmap + 28) + rect.top * _width + rect.left;
- // HACK: we pretty much ignore the plane rect and x, y...
- drawTextBitmapInternal(0, 0, Common::Rect(20, 390, 600, 460), textObject, hunkId);
+ // NOTE: Not fully disassembled, but this should be right
+ int16 rectWidth = targetRect.width();
+ int16 sidesHeight = targetRect.height() - size * 2;
+ int16 centerWidth = rectWidth - size * 2;
+ int16 stride = _width - rectWidth;
+
+ for (int16 y = 0; y < size; ++y) {
+ memset(pixels, color, rectWidth);
+ pixels += _width;
+ }
+ for (int16 y = 0; y < sidesHeight; ++y) {
+ for (int16 x = 0; x < size; ++x) {
+ *pixels++ = color;
+ }
+ pixels += centerWidth;
+ for (int16 x = 0; x < size; ++x) {
+ *pixels++ = color;
+ }
+ pixels += stride;
+ }
+ for (int16 y = 0; y < size; ++y) {
+ memset(pixels, color, rectWidth);
+ pixels += _width;
+ }
}
-void GfxText32::drawTextBitmapInternal(int16 x, int16 y, Common::Rect planeRect, reg_t textObject, reg_t hunkId) {
- int16 backColor = (int16)readSelectorValue(_segMan, textObject, SELECTOR(back));
- // Sanity check: Check if the hunk is set. If not, either the game scripts
- // didn't set it, or an old saved game has been loaded, where it wasn't set.
- if (hunkId.isNull())
- return;
+void GfxText32::drawChar(const char charIndex) {
+ byte *bitmap = _segMan->getHunkPointer(_bitmap);
+ byte *pixels = bitmap + READ_SCI11ENDIAN_UINT32(bitmap + 28);
- // Negative coordinates indicate that text shouldn't be displayed
- if (x < 0 || y < 0)
- return;
+ _font->drawToBuffer(charIndex, _drawPosition.y, _drawPosition.x, _foreColor, _dimmed, pixels, _width, _height);
+ _drawPosition.x += _font->getCharWidth(charIndex);
+}
- byte *memoryPtr = _segMan->getHunkPointer(hunkId);
+uint16 GfxText32::getCharWidth(const char charIndex, const bool doScaling) const {
+ uint16 width = _font->getCharWidth(charIndex);
+ if (doScaling) {
+ width = scaleUpWidth(width);
+ }
+ return width;
+}
- if (!memoryPtr) {
- // Happens when restoring in some SCI32 games (e.g. SQ6).
- // Commented out to reduce console spam
- //warning("Attempt to draw an invalid text bitmap");
+void GfxText32::drawTextBox() {
+ if (_text.size() == 0) {
return;
}
- byte *surface = memoryPtr + BITMAP_HEADER_SIZE;
+ const char *text = _text.c_str();
+ const char *sourceText = text;
+ int16 textRectWidth = _textRect.width();
+ _drawPosition.y = _textRect.top;
+ uint charIndex = 0;
+ if (getLongest(&charIndex, textRectWidth) == 0) {
+ error("DrawTextBox GetLongest=0");
+ }
+
+ charIndex = 0;
+ uint nextCharIndex = 0;
+ while (*text != '\0') {
+ _drawPosition.x = _textRect.left;
+
+ uint length = getLongest(&nextCharIndex, textRectWidth);
+ int16 textWidth = getTextWidth(charIndex, length);
+
+ if (_alignment == kTextAlignCenter) {
+ _drawPosition.x += (textRectWidth - textWidth) / 2;
+ } else if (_alignment == kTextAlignRight) {
+ _drawPosition.x += textRectWidth - textWidth;
+ }
+
+ drawText(charIndex, length);
+ charIndex = nextCharIndex;
+ text = sourceText + charIndex;
+ _drawPosition.y += _font->getHeight();
+ }
+}
+
+void GfxText32::drawTextBox(const Common::String &text) {
+ _text = text;
+ drawTextBox();
+}
+
+void GfxText32::drawText(const uint index, uint length) {
+ assert(index + length <= _text.size());
+
+ // NOTE: This draw loop implementation is somewhat different than the
+ // implementation in the actual engine, but should be accurate. Primarily
+ // the changes revolve around eliminating some extra temporaries and
+ // fixing the logic to match.
+ const char *text = _text.c_str() + index;
+ while (length-- > 0) {
+ char currentChar = *text++;
+
+ if (currentChar == '|') {
+ const char controlChar = *text++;
+ --length;
+
+ if (length == 0) {
+ return;
+ }
+
+ if (controlChar == 'a' || controlChar == 'c' || controlChar == 'f') {
+ uint16 value = 0;
+
+ while (length > 0) {
+ const char valueChar = *text;
+ if (valueChar < '0' || valueChar > '9') {
+ break;
+ }
+
+ ++text;
+ --length;
+ value = 10 * value + (valueChar - '0');
+ }
+
+ if (length == 0) {
+ return;
+ }
+
+ if (controlChar == 'a') {
+ _alignment = (TextAlign)value;
+ } else if (controlChar == 'c') {
+ _foreColor = value;
+ } else if (controlChar == 'f') {
+ setFont(value);
+ }
+ }
+
+ while (length > 0 && *text != '|') {
+ ++text;
+ --length;
+ }
+ } else {
+ drawChar(currentChar);
+ }
+ }
+}
+
+void GfxText32::invertRect(const reg_t bitmap, int16 bitmapStride, const Common::Rect &rect, const uint8 foreColor, const uint8 backColor, const bool doScaling) {
+ Common::Rect targetRect = rect;
+ if (doScaling) {
+ bitmapStride = bitmapStride * _scaledWidth / g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth;
+ targetRect = scaleRect(rect);
+ }
+
+ byte *bitmapData = _segMan->getHunkPointer(bitmap);
- int curByte = 0;
- int16 skipColor = (int16)readSelectorValue(_segMan, textObject, SELECTOR(skip));
- uint16 textX = planeRect.left + x;
- uint16 textY = planeRect.top + y;
- // Get totalWidth, totalHeight
- uint16 width = READ_LE_UINT16(memoryPtr);
- uint16 height = READ_LE_UINT16(memoryPtr + 2);
+ // NOTE: SCI code is super weird here; it seems to be trying to look at the
+ // entire size of the bitmap including the header, instead of just the pixel
+ // data size. We just look at the pixel size. This function generally is an
+ // odd duck since the stride dimension for a bitmap is built in to the bitmap
+ // header, so perhaps it was once an unheadered bitmap format and this
+ // function was never updated to match? Or maybe they exploit the
+ // configurable stride length somewhere else to do stair stepping inverts...
+ uint32 invertSize = targetRect.height() * bitmapStride + targetRect.width();
+ uint32 bitmapSize = READ_SCI11ENDIAN_UINT32(bitmapData + 12);
- // Upscale the coordinates/width if the fonts are already upscaled
- if (_screen->fontIsUpscaled()) {
- textX = textX * _screen->getDisplayWidth() / _screen->getWidth();
- textY = textY * _screen->getDisplayHeight() / _screen->getHeight();
+ if (invertSize >= bitmapSize) {
+ error("InvertRect too big: %u >= %u", invertSize, bitmapSize);
}
- bool translucent = (skipColor == -1 && backColor == -1);
+ // NOTE: Actual engine just added the bitmap header size hardcoded here
+ byte *pixel = bitmapData + READ_SCI11ENDIAN_UINT32(bitmapData + 28) + bitmapStride * targetRect.top + targetRect.left;
- for (int curY = 0; curY < height; curY++) {
- for (int curX = 0; curX < width; curX++) {
- byte pixel = surface[curByte++];
- if ((!translucent && pixel != skipColor && pixel != backColor) ||
- (translucent && pixel != 0xFF))
- _screen->putFontPixel(textY, curX + textX, curY, pixel);
+ int16 stride = bitmapStride - targetRect.width();
+ int16 targetHeight = targetRect.height();
+ int16 targetWidth = targetRect.width();
+
+ for (int16 y = 0; y < targetHeight; ++y) {
+ for (int16 x = 0; x < targetWidth; ++x) {
+ if (*pixel == foreColor) {
+ *pixel = backColor;
+ } else if (*pixel == backColor) {
+ *pixel = foreColor;
+ }
+
+ ++pixel;
}
+
+ pixel += stride;
}
}
-int16 GfxText32::GetLongest(const char *text, int16 maxWidth, GfxFont *font) {
- uint16 curChar = 0;
- int16 maxChars = 0, curCharCount = 0;
- uint16 width = 0;
-
- while (width <= maxWidth) {
- curChar = (*(const byte *)text++);
-
- switch (curChar) {
- // We need to add 0xD, 0xA and 0xD 0xA to curCharCount and then exit
- // which means, we split text like
- // 'Mature, experienced software analyst available.' 0xD 0xA
- // 'Bug installation a proven speciality. "No version too clean."' (normal game text, this is from lsl2)
- // and 0xA '-------' 0xA (which is the official sierra subtitle separator)
- // Sierra did it the same way.
- case 0xD:
- // Check, if 0xA is following, if so include it as well
- if ((*(const unsigned char *)text) == 0xA)
- curCharCount++;
- // it's meant to pass through here
- case 0xA:
- curCharCount++;
- // and it's also meant to pass through here
- case 0:
- return curCharCount;
- case ' ':
- maxChars = curCharCount; // return count up to (but not including) breaking space
- break;
+uint GfxText32::getLongest(uint *charIndex, const int16 width) {
+ assert(width > 0);
+
+ uint testLength = 0;
+ uint length = 0;
+
+ const uint initialCharIndex = *charIndex;
+
+ // The index of the next word after the last word break
+ uint lastWordBreakIndex = *charIndex;
+
+ const char *text = _text.c_str() + *charIndex;
+
+ char currentChar;
+ while ((currentChar = *text++) != '\0') {
+ // NOTE: In the original engine, the font, color, and alignment were
+ // reset here to their initial values
+
+ // The text to render contains a line break; stop at the line break
+ if (currentChar == '\r' || currentChar == '\n') {
+ // Skip the rest of the line break if it is a Windows-style
+ // \r\n or non-standard \n\r
+ // NOTE: In the original engine, the `text` pointer had not been
+ // advanced yet so the indexes used to access characters were
+ // one higher
+ if (
+ (currentChar == '\r' && text[0] == '\n') ||
+ (currentChar == '\n' && text[0] == '\r' && text[1] != '\n')
+ ) {
+ ++*charIndex;
+ }
+
+ // We are at the end of a line but the last word in the line made
+ // it too wide to fit in the text area; return up to the previous
+ // word
+ if (length && getTextWidth(initialCharIndex, testLength) > width) {
+ *charIndex = lastWordBreakIndex;
+ return length;
+ }
+
+ // Skip the line break and return all text seen up to now
+ // NOTE: In original engine, the font, color, and alignment were
+ // reset, then getTextWidth was called to use its side-effects to
+ // set font, color, and alignment according to the text from
+ // `initialCharIndex` to `testLength`
+ ++*charIndex;
+ return testLength;
+ } else if (currentChar == ' ') {
+ // The last word in the line made it too wide to fit in the text area;
+ // return up to the previous word, then collapse the whitespace
+ // between that word and its next sibling word into the line break
+ if (getTextWidth(initialCharIndex, testLength) > width) {
+ *charIndex = lastWordBreakIndex;
+ const char *nextChar = _text.c_str() + lastWordBreakIndex;
+ while (*nextChar++ == ' ') {
+ ++*charIndex;
+ }
+
+ // NOTE: In original engine, the font, color, and alignment were
+ // set here to the values that were seen at the last space character
+ return length;
+ }
+
+ // NOTE: In the original engine, the values of _fontId, _foreColor,
+ // and _alignment were stored for use in the return path mentioned
+ // just above here
+
+ // We found a word break that was within the text area, memorise it
+ // and continue processing. +1 on the character index because it has
+ // not been incremented yet so currently points to the word break
+ // and not the word after the break
+ length = testLength;
+ lastWordBreakIndex = *charIndex + 1;
+ }
+
+ // In the middle of a line, keep processing
+ ++*charIndex;
+ ++testLength;
+
+ // NOTE: In the original engine, the font, color, and alignment were
+ // reset here to their initial values
+
+ // The text to render contained no word breaks yet but is already too
+ // wide for the text area; just split the word in half at the point
+ // where it overflows
+ if (length == 0 && getTextWidth(initialCharIndex, testLength) > width) {
+ *charIndex = --testLength + lastWordBreakIndex;
+ return testLength;
}
- if (width + font->getCharWidth(curChar) > maxWidth)
- break;
- width += font->getCharWidth(curChar);
- curCharCount++;
}
- return maxChars;
+ // The complete text to render was a single word, or was narrower than
+ // the text area, so return the entire line
+ if (length == 0 || getTextWidth(initialCharIndex, testLength) <= width) {
+ // NOTE: In original engine, the font, color, and alignment were
+ // reset, then getTextWidth was called to use its side-effects to
+ // set font, color, and alignment according to the text from
+ // `initialCharIndex` to `testLength`
+ return testLength;
+ }
+
+ // The last word in the line made it wider than the text area, so return
+ // up to the penultimate word
+ *charIndex = lastWordBreakIndex;
+ return length;
}
-void GfxText32::kernelTextSize(const char *text, int16 font, int16 maxWidth, int16 *textWidth, int16 *textHeight) {
- Common::Rect rect(0, 0, 0, 0);
- Size(rect, text, font, maxWidth);
- *textWidth = rect.width();
- *textHeight = rect.height();
+int16 GfxText32::getTextWidth(const uint index, uint length) const {
+ int16 width = 0;
+
+ const char *text = _text.c_str() + index;
+
+ GfxFont *font = _font;
+
+ char currentChar = *text++;
+ while (length > 0 && currentChar != '\0') {
+ // Control codes are in the format `|<code><value>|`
+ if (currentChar == '|') {
+ // NOTE: Original engine code changed the global state of the
+ // FontMgr here upon encountering any color, alignment, or
+ // font control code.
+ // To avoid requiring all callers to manually restore these
+ // values on every call, we ignore control codes other than
+ // font change (since alignment and color do not change the
+ // width of characters), and simply update the font pointer
+ // on stack instead of the member property font.
+ currentChar = *text++;
+ --length;
+
+ if (length > 0 && currentChar == 'f') {
+ GuiResourceId fontId = 0;
+ do {
+ currentChar = *text++;
+ --length;
+
+ fontId = fontId * 10 + currentChar - '0';
+ } while (length > 0 && currentChar >= '0' && currentChar <= '9');
+
+ if (length > 0) {
+ font = _cache->getFont(fontId);
+ }
+ }
+
+ // Forward through any more unknown control character data
+ while (length > 0 && currentChar != '|') {
+ ++text;
+ --length;
+ }
+ } else {
+ width += font->getCharWidth(currentChar);
+ }
+
+ currentChar = *text++;
+ --length;
+ }
+
+ return width;
}
-void GfxText32::StringWidth(const char *str, GuiResourceId fontId, int16 &textWidth, int16 &textHeight) {
- Width(str, 0, (int16)strlen(str), fontId, textWidth, textHeight, true);
+int16 GfxText32::getTextWidth(const Common::String &text, const uint index, const uint length) {
+ _text = text;
+ return scaleUpWidth(getTextWidth(index, length));
}
-void GfxText32::Width(const char *text, int16 from, int16 len, GuiResourceId fontId, int16 &textWidth, int16 &textHeight, bool restoreFont) {
- byte curChar;
- textWidth = 0; textHeight = 0;
-
- GfxFont *font = _cache->getFont(fontId);
-
- if (font) {
- text += from;
- while (len--) {
- curChar = (*(const byte *)text++);
- switch (curChar) {
- case 0x0A:
- case 0x0D:
- textHeight = MAX<int16> (textHeight, font->getHeight());
- break;
- case 0x7C:
- warning("Code processing isn't implemented in SCI32");
- break;
- default:
- textHeight = MAX<int16> (textHeight, font->getHeight());
- textWidth += font->getCharWidth(curChar);
- break;
+Common::Rect GfxText32::getTextSize(const Common::String &text, int16 maxWidth, bool doScaling) {
+ // NOTE: Like most of the text rendering code, this function was pretty
+ // weird in the original engine. The initial result rectangle was actually
+ // a 1x1 rectangle (0, 0, 0, 0), which was then "fixed" after the main
+ // text size loop finished running by subtracting 1 from the right and
+ // bottom edges. Like other functions in SCI32, this has been converted
+ // to use exclusive rects with inclusive rounding.
+
+ Common::Rect result;
+
+ int16 scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth;
+ int16 scriptHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight;
+
+ maxWidth = maxWidth * _scaledWidth / scriptWidth;
+
+ _text = text;
+
+ if (maxWidth >= 0) {
+ if (maxWidth == 0) {
+ // TODO: This was hardcoded to 192, but guessing
+ // that it was originally 60% of the scriptWidth
+ // before the compiler took over.
+ // Verify this by looking at a game that uses a
+ // scriptWidth other than 320, like LSL7
+ maxWidth = _scaledWidth * (scriptWidth * 0.6) / scriptWidth;
+ }
+
+ result.right = maxWidth;
+
+ int16 textWidth = 0;
+ if (_text.size() > 0) {
+ const char *rawText = _text.c_str();
+ const char *sourceText = rawText;
+ uint charIndex = 0;
+ uint nextCharIndex = 0;
+ while (*rawText != '\0') {
+ uint length = getLongest(&nextCharIndex, result.width());
+ textWidth = MAX(textWidth, getTextWidth(charIndex, length));
+ charIndex = nextCharIndex;
+ rawText = sourceText + charIndex;
+ // TODO: Due to getLongest and getTextWidth not having side
+ // effects, it is possible that the currently loaded font's
+ // height is wrong for this line if it was changed inline
+ result.bottom += _font->getHeight();
}
}
+
+ if (textWidth < maxWidth) {
+ result.right = textWidth;
+ }
+ } else {
+ result.right = getTextWidth(0, 10000);
+ // NOTE: In the original engine code, the bottom was not decremented
+ // by 1, which means that the rect was actually a pixel taller than
+ // the height of the font. This was not the case in the other branch,
+ // which decremented the bottom by 1 at the end of the loop.
+ result.bottom = _font->getHeight() + 1;
}
+
+ if (doScaling) {
+ // NOTE: The original engine code also scaled top/left but these are
+ // always zero so there is no reason to do that.
+ result.right = ((result.right - 1) * scriptWidth + _scaledWidth - 1) / _scaledWidth + 1;
+ result.bottom = ((result.bottom - 1) * scriptHeight + _scaledHeight - 1) / _scaledHeight + 1;
+ }
+
+ return result;
}
-int16 GfxText32::Size(Common::Rect &rect, const char *text, GuiResourceId fontId, int16 maxWidth) {
- int16 charCount;
- int16 maxTextWidth = 0, textWidth;
- int16 totalHeight = 0, textHeight;
+void GfxText32::erase(const Common::Rect &rect, const bool doScaling) {
+ Common::Rect targetRect = doScaling ? scaleRect(rect) : rect;
- // Adjust maxWidth if we're using an upscaled font
- if (_screen->fontIsUpscaled())
- maxWidth = maxWidth * _screen->getDisplayWidth() / _screen->getWidth();
+ byte *bitmap = _segMan->getHunkPointer(_bitmap);
+ byte *pixels = bitmap + READ_SCI11ENDIAN_UINT32(bitmap + 28);
- rect.top = rect.left = 0;
- GfxFont *font = _cache->getFont(fontId);
+ // NOTE: There is an extra optimisation within the SCI code to
+ // do a single memset if the scaledRect is the same size as
+ // the bitmap, not implemented here.
+ Buffer buffer(_width, _height, pixels);
+ buffer.fillRect(targetRect, _backColor);
+}
- if (maxWidth < 0) { // force output as single line
- StringWidth(text, fontId, textWidth, textHeight);
- rect.bottom = textHeight;
- rect.right = textWidth;
- } else {
- // rect.right=found widest line with RTextWidth and GetLongest
- // rect.bottom=num. lines * GetPointSize
- rect.right = (maxWidth ? maxWidth : 192);
- const char *curPos = text;
- while (*curPos) {
- charCount = GetLongest(curPos, rect.right, font);
- if (charCount == 0)
- break;
- Width(curPos, 0, charCount, fontId, textWidth, textHeight, false);
- maxTextWidth = MAX(textWidth, maxTextWidth);
- totalHeight += textHeight;
- curPos += charCount;
- while (*curPos == ' ')
- curPos++; // skip over breaking spaces
- }
- rect.bottom = totalHeight;
- rect.right = maxWidth ? maxWidth : MIN(rect.right, maxTextWidth);
+int16 GfxText32::getStringWidth(const Common::String &text) {
+ return getTextWidth(text, 0, 10000);
+}
+
+int16 GfxText32::getTextCount(const Common::String &text, const uint index, const Common::Rect &textRect, const bool doScaling) {
+ const int16 scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth;
+ const int16 scriptHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight;
+
+ Common::Rect scaledRect(textRect);
+ if (doScaling) {
+ mulinc(scaledRect, Ratio(_scaledWidth, scriptWidth), Ratio(_scaledHeight, scriptHeight));
}
- // Adjust the width/height if we're using an upscaled font
- // for the scripts
- if (_screen->fontIsUpscaled()) {
- rect.right = rect.right * _screen->getWidth() / _screen->getDisplayWidth();
- rect.bottom = rect.bottom * _screen->getHeight() / _screen->getDisplayHeight();
+ Common::String oldText = _text;
+ _text = text;
+
+ uint charIndex = index;
+ int16 maxWidth = scaledRect.width();
+ int16 lineCount = (scaledRect.height() - 2) / _font->getHeight();
+ while (lineCount--) {
+ getLongest(&charIndex, maxWidth);
}
- return rect.right;
+ _text = oldText;
+ return charIndex - index;
}
+int16 GfxText32::getTextCount(const Common::String &text, const uint index, const GuiResourceId fontId, const Common::Rect &textRect, const bool doScaling) {
+ setFont(fontId);
+ return getTextCount(text, index, textRect, doScaling);
+}
+
+
} // End of namespace Sci
diff --git a/engines/sci/graphics/text32.h b/engines/sci/graphics/text32.h
index 7ba7df50e4..5768ea0c59 100644
--- a/engines/sci/graphics/text32.h
+++ b/engines/sci/graphics/text32.h
@@ -23,34 +23,455 @@
#ifndef SCI_GRAPHICS_TEXT32_H
#define SCI_GRAPHICS_TEXT32_H
+#include "sci/graphics/celobj32.h"
+#include "sci/graphics/frameout.h"
+
namespace Sci {
+enum TextAlign {
+ kTextAlignLeft = 0,
+ kTextAlignCenter = 1,
+ kTextAlignRight = 2
+};
+
+enum BitmapFlags {
+ kBitmapRemap = 2
+};
+
+#define BITMAP_PROPERTY(size, property, offset)\
+inline uint##size get##property() const {\
+ return READ_SCI11ENDIAN_UINT##size(_bitmap + (offset));\
+}\
+inline void set##property(uint##size value) {\
+ WRITE_SCI11ENDIAN_UINT##size(_bitmap + (offset), (value));\
+}
+
/**
- * Text32 class, handles text calculation and displaying of text for SCI2, SCI21 and SCI3 games
+ * A convenience class for creating and modifying in-memory
+ * bitmaps.
*/
-class GfxText32 {
+class BitmapResource {
+ byte *_bitmap;
+ reg_t _object;
+
+ /**
+ * Gets the size of the bitmap header for the current
+ * engine version.
+ */
+ static inline uint16 getBitmapHeaderSize() {
+ // TODO: These values are accurate for each engine, but there may be no reason
+ // to not simply just always use size 40, since SCI2.1mid does not seem to
+ // actually store any data above byte 40, and SCI2 did not allow bitmaps with
+ // scaling resolutions other than the default (320x200). Perhaps SCI3 used
+ // the extra bytes, or there is some reason why they tried to align the header
+ // size with other headers like pic headers?
+// uint32 bitmapHeaderSize;
+// if (getSciVersion() >= SCI_VERSION_2_1_MIDDLE) {
+// bitmapHeaderSize = 46;
+// } else if (getSciVersion() == SCI_VERSION_2_1_EARLY) {
+// bitmapHeaderSize = 40;
+// } else {
+// bitmapHeaderSize = 36;
+// }
+// return bitmapHeaderSize;
+ return 46;
+ }
+
+ /**
+ * Gets the byte size of a bitmap with the given width
+ * and height.
+ */
+ static inline uint32 getBitmapSize(const uint16 width, const uint16 height) {
+ return width * height + getBitmapHeaderSize();
+ }
+
public:
- GfxText32(SegManager *segMan, GfxCache *fonts, GfxScreen *screen);
- ~GfxText32();
- reg_t createTextBitmap(reg_t textObject, uint16 maxWidth = 0, uint16 maxHeight = 0, reg_t prevHunk = NULL_REG);
- reg_t createScrollTextBitmap(Common::String text, reg_t textObject, uint16 maxWidth = 0, uint16 maxHeight = 0, reg_t prevHunk = NULL_REG);
- void drawTextBitmap(int16 x, int16 y, Common::Rect planeRect, reg_t textObject);
- void drawScrollTextBitmap(reg_t textObject, reg_t hunkId, uint16 x, uint16 y);
- void disposeTextBitmap(reg_t hunkId);
- int16 GetLongest(const char *text, int16 maxWidth, GfxFont *font);
+ /**
+ * Create a bitmap resource for an existing bitmap.
+ * Ownership of the bitmap is retained by the caller.
+ */
+ inline BitmapResource(reg_t bitmap) :
+ _bitmap(g_sci->getEngineState()->_segMan->getHunkPointer(bitmap)),
+ _object(bitmap) {
+ if (_bitmap == nullptr || getUncompressedDataOffset() != getBitmapHeaderSize()) {
+ error("Invalid Text bitmap %04x:%04x", PRINT_REG(bitmap));
+ }
+ }
- void kernelTextSize(const char *text, int16 font, int16 maxWidth, int16 *textWidth, int16 *textHeight);
+ /**
+ * Allocates and initialises a new bitmap in the given
+ * segment manager.
+ */
+ inline BitmapResource(SegManager *segMan, const int16 width, const int16 height, const uint8 skipColor, const int16 displaceX, const int16 displaceY, const int16 scaledWidth, const int16 scaledHeight, const uint32 hunkPaletteOffset, const bool remap) {
-private:
- reg_t createTextBitmapInternal(Common::String &text, reg_t textObject, uint16 maxWidth, uint16 maxHeight, reg_t hunkId);
- void drawTextBitmapInternal(int16 x, int16 y, Common::Rect planeRect, reg_t textObject, reg_t hunkId);
- int16 Size(Common::Rect &rect, const char *text, GuiResourceId fontId, int16 maxWidth);
- void Width(const char *text, int16 from, int16 len, GuiResourceId orgFontId, int16 &textWidth, int16 &textHeight, bool restoreFont);
- void StringWidth(const char *str, GuiResourceId orgFontId, int16 &textWidth, int16 &textHeight);
+ _object = segMan->allocateHunkEntry("Bitmap()", getBitmapSize(width, height));
+ _bitmap = segMan->getHunkPointer(_object);
+
+ const uint16 bitmapHeaderSize = getBitmapHeaderSize();
+
+ setWidth(width);
+ setHeight(height);
+ setDisplace(Common::Point(displaceX, displaceY));
+ setSkipColor(skipColor);
+ _bitmap[9] = 0;
+ WRITE_SCI11ENDIAN_UINT16(_bitmap + 10, 0);
+ setRemap(remap);
+ setDataSize(width * height);
+ WRITE_SCI11ENDIAN_UINT32(_bitmap + 16, 0);
+ setHunkPaletteOffset(hunkPaletteOffset);
+ setDataOffset(bitmapHeaderSize);
+ setUncompressedDataOffset(bitmapHeaderSize);
+ setControlOffset(0);
+ setScaledWidth(scaledWidth);
+ setScaledHeight(scaledHeight);
+ }
+
+ reg_t getObject() const {
+ return _object;
+ }
+
+ BITMAP_PROPERTY(16, Width, 0);
+ BITMAP_PROPERTY(16, Height, 2);
+
+ inline Common::Point getDisplace() const {
+ return Common::Point(
+ (int16)READ_SCI11ENDIAN_UINT16(_bitmap + 4),
+ (int16)READ_SCI11ENDIAN_UINT16(_bitmap + 6)
+ );
+ }
+
+ inline void setDisplace(const Common::Point &displace) {
+ WRITE_SCI11ENDIAN_UINT16(_bitmap + 4, (uint16)displace.x);
+ WRITE_SCI11ENDIAN_UINT16(_bitmap + 6, (uint16)displace.y);
+ }
+
+ inline uint8 getSkipColor() const {
+ return _bitmap[8];
+ }
+
+ inline void setSkipColor(const uint8 skipColor) {
+ _bitmap[8] = skipColor;
+ }
+
+ inline bool getRemap() const {
+ return READ_SCI11ENDIAN_UINT16(_bitmap + 10) & kBitmapRemap;
+ }
+
+ inline void setRemap(const bool remap) {
+ uint16 flags = READ_SCI11ENDIAN_UINT16(_bitmap + 10);
+ if (remap) {
+ flags |= kBitmapRemap;
+ } else {
+ flags &= ~kBitmapRemap;
+ }
+ WRITE_SCI11ENDIAN_UINT16(_bitmap + 10, flags);
+ }
+ BITMAP_PROPERTY(32, DataSize, 12);
+
+ inline uint32 getHunkPaletteOffset() const {
+ return READ_SCI11ENDIAN_UINT32(_bitmap + 20);
+ }
+
+ void setHunkPaletteOffset(uint32 hunkPaletteOffset) {
+ if (hunkPaletteOffset) {
+ hunkPaletteOffset += getBitmapHeaderSize();
+ }
+
+ WRITE_SCI11ENDIAN_UINT32(_bitmap + 20, hunkPaletteOffset);
+ }
+
+ BITMAP_PROPERTY(32, DataOffset, 24);
+
+ // NOTE: This property is used as a "magic number" for
+ // validating that a block of memory is a valid bitmap,
+ // and so is always set to the size of the header.
+ BITMAP_PROPERTY(32, UncompressedDataOffset, 28);
+
+ // NOTE: This property always seems to be zero
+ BITMAP_PROPERTY(32, ControlOffset, 32);
+
+ inline uint16 getScaledWidth() const {
+ if (getDataOffset() >= 40) {
+ return READ_SCI11ENDIAN_UINT16(_bitmap + 36);
+ }
+
+ // SCI2 bitmaps did not have scaling ability
+ return 320;
+ }
+
+ inline void setScaledWidth(uint16 scaledWidth) {
+ if (getDataOffset() >= 40) {
+ WRITE_SCI11ENDIAN_UINT16(_bitmap + 36, scaledWidth);
+ }
+ }
+
+ inline uint16 getScaledHeight() const {
+ if (getDataOffset() >= 40) {
+ return READ_SCI11ENDIAN_UINT16(_bitmap + 38);
+ }
+
+ // SCI2 bitmaps did not have scaling ability
+ return 200;
+ }
+
+ inline void setScaledHeight(uint16 scaledHeight) {
+ if (getDataOffset() >= 40) {
+ WRITE_SCI11ENDIAN_UINT16(_bitmap + 38, scaledHeight);
+ }
+ }
+
+ inline byte *getPixels() {
+ return _bitmap + getUncompressedDataOffset();
+ }
+};
+
+class GfxFont;
+
+/**
+ * This class handles text calculation and rendering for
+ * SCI32 games. The text calculation system in SCI32 is
+ * nearly the same as SCI16, which means this class behaves
+ * similarly. Notably, GfxText32 maintains drawing
+ * parameters across multiple calls.
+ */
+class GfxText32 {
+private:
SegManager *_segMan;
GfxCache *_cache;
- GfxScreen *_screen;
+
+ /**
+ * The resource ID of the default font used by the game.
+ *
+ * @todo Check all SCI32 games to learn what their
+ * default font is.
+ */
+ static int16 _defaultFontId;
+
+ /**
+ * The width and height of the currently active text
+ * bitmap, in text-system coordinates.
+ *
+ * @note These are unsigned in the actual engine.
+ */
+ int16 _width, _height;
+
+ /**
+ * The color used to draw text.
+ */
+ uint8 _foreColor;
+
+ /**
+ * The background color of the text box.
+ */
+ uint8 _backColor;
+
+ /**
+ * The transparent color of the text box. Used when
+ * compositing the bitmap onto the screen.
+ */
+ uint8 _skipColor;
+
+ /**
+ * The rect where the text is drawn within the bitmap.
+ * This rect is clipped to the dimensions of the bitmap.
+ */
+ Common::Rect _textRect;
+
+ /**
+ * The text being drawn to the currently active text
+ * bitmap.
+ */
+ Common::String _text;
+
+ /**
+ * The font being used to draw the text.
+ */
+ GuiResourceId _fontId;
+
+ /**
+ * The color of the text box border.
+ */
+ int16 _borderColor;
+
+ /**
+ * TODO: Document
+ */
+ bool _dimmed;
+
+ /**
+ * The text alignment for the drawn text.
+ */
+ TextAlign _alignment;
+
+ int16 _field_20;
+
+ /**
+ * TODO: Document
+ */
+ int16 _field_22;
+
+ int _field_2C, _field_30, _field_34, _field_38;
+
+ int16 _field_3C;
+
+ /**
+ * The position of the text draw cursor.
+ */
+ Common::Point _drawPosition;
+
+ void drawFrame(const Common::Rect &rect, const int16 size, const uint8 color, const bool doScaling);
+
+ void drawChar(const char charIndex);
+ void drawText(const uint index, uint length);
+
+ /**
+ * Gets the length of the longest run of text available
+ * within the currently loaded text, starting from the
+ * given `charIndex` and running for up to `maxWidth`
+ * pixels. Returns the number of characters that can be
+ * written, and mutates the value pointed to by
+ * `charIndex` to point to the index of the next
+ * character to render.
+ */
+ uint getLongest(uint *charIndex, const int16 maxWidth);
+
+ /**
+ * Gets the pixel width of a substring of the currently
+ * loaded text, without scaling.
+ */
+ int16 getTextWidth(const uint index, uint length) const;
+
+ inline Common::Rect scaleRect(const Common::Rect &rect) {
+ Common::Rect scaledRect(rect);
+ int16 scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth;
+ int16 scriptHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight;
+ Ratio scaleX(_scaledWidth, scriptWidth);
+ Ratio scaleY(_scaledHeight, scriptHeight);
+ mulinc(scaledRect, scaleX, scaleY);
+ return scaledRect;
+ }
+
+public:
+ GfxText32(SegManager *segMan, GfxCache *fonts);
+
+ /**
+ * The memory handle of the currently active bitmap.
+ */
+ reg_t _bitmap;
+
+ /**
+ * The size of the x-dimension of the coordinate system
+ * used by the text renderer.
+ */
+ int16 _scaledWidth;
+
+ /**
+ * The size of the y-dimension of the coordinate system
+ * used by the text renderer.
+ */
+ int16 _scaledHeight;
+
+ /**
+ * The currently active font resource used to write text
+ * into the bitmap.
+ *
+ * @note SCI engine builds the font table directly
+ * inside of FontMgr; we use GfxFont instead.
+ */
+ GfxFont *_font;
+
+ /**
+ * Creates a plain font bitmap with a flat color
+ * background.
+ */
+ reg_t createFontBitmap(int16 width, int16 height, const Common::Rect &rect, const Common::String &text, const uint8 foreColor, const uint8 backColor, const uint8 skipColor, const GuiResourceId fontId, TextAlign alignment, const int16 borderColor, bool dimmed, const bool doScaling);
+
+ /**
+ * Creates a font bitmap with a view background.
+ */
+ reg_t createFontBitmap(const CelInfo32 &celInfo, const Common::Rect &rect, const Common::String &text, const int16 foreColor, const int16 backColor, const GuiResourceId fontId, const int16 skipColor, const int16 borderColor, const bool dimmed);
+
+ /**
+ * Creates a font bitmap with a title.
+ */
+ reg_t createTitledBitmap(const int16 width, const int16 height, const Common::Rect &textRect, const Common::String &text, const int16 foreColor, const int16 backColor, const int16 skipColor, const GuiResourceId fontId, const TextAlign alignment, const int16 borderColor, Common::String &title, const int16 titleForeColor, const int16 titleBackColor, const GuiResourceId titleFontId, const bool doScaling);
+
+ inline int scaleUpWidth(int value) const {
+ const int scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth;
+ return (value * scriptWidth + _scaledWidth - 1) / _scaledWidth;
+ }
+
+ inline int scaleUpHeight(int value) const {
+ const int scriptHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight;
+ return (value * scriptHeight + _scaledHeight - 1) / _scaledHeight;
+ }
+
+ /**
+ * Draws the text to the bitmap.
+ */
+ void drawTextBox();
+
+ /**
+ * Draws the given text to the bitmap.
+ *
+ * @note The original engine holds a reference to a
+ * shared string which lets the text be updated from
+ * outside of the font manager. Instead, we give this
+ * extra signature to send the text to draw.
+ *
+ * TODO: Use shared string instead?
+ */
+ void drawTextBox(const Common::String &text);
+
+ /**
+ * Erases the given rect by filling with the background
+ * color.
+ */
+ void erase(const Common::Rect &rect, const bool doScaling);
+
+ void invertRect(const reg_t bitmap, const int16 bitmapStride, const Common::Rect &rect, const uint8 foreColor, const uint8 backColor, const bool doScaling);
+
+ /**
+ * Sets the font to be used for rendering and
+ * calculation of text dimensions.
+ */
+ void setFont(const GuiResourceId fontId);
+
+ /**
+ * Gets the width of a character.
+ */
+ uint16 getCharWidth(const char charIndex, const bool doScaling) const;
+
+ /**
+ * Retrieves the width and height of a block of text.
+ */
+ Common::Rect getTextSize(const Common::String &text, const int16 maxWidth, bool doScaling);
+
+ /**
+ * Gets the pixel width of a substring of the currently
+ * loaded text, with scaling.
+ */
+ int16 getTextWidth(const Common::String &text, const uint index, const uint length);
+
+ /**
+ * Retrieves the width of a line of text.
+ */
+ int16 getStringWidth(const Common::String &text);
+
+ /**
+ * Gets the number of characters of `text`, starting
+ * from `index`, that can be safely rendered into
+ * `textRect`.
+ */
+ int16 getTextCount(const Common::String &text, const uint index, const Common::Rect &textRect, const bool doScaling);
+
+ /**
+ * Gets the number of characters of `text`, starting
+ * from `index`, that can be safely rendered into
+ * `textRect` using the given font.
+ */
+ int16 getTextCount(const Common::String &text, const uint index, const GuiResourceId fontId, const Common::Rect &textRect, const bool doScaling);
};
} // End of namespace Sci
diff --git a/engines/sci/graphics/view.cpp b/engines/sci/graphics/view.cpp
index 2ee18b5c9a..1939e66179 100644
--- a/engines/sci/graphics/view.cpp
+++ b/engines/sci/graphics/view.cpp
@@ -25,6 +25,7 @@
#include "sci/engine/state.h"
#include "sci/graphics/screen.h"
#include "sci/graphics/palette.h"
+#include "sci/graphics/remap.h"
#include "sci/graphics/coordadjuster.h"
#include "sci/graphics/view.h"
@@ -833,19 +834,6 @@ void GfxView::draw(const Common::Rect &rect, const Common::Rect &clipRect, const
bitmap += (clipRect.top - rect.top) * celWidth + (clipRect.left - rect.left);
- // WORKAROUND: EcoQuest French and German draw the fish and anemone sprites
- // with priority 15 in scene 440. Afterwards, a dialog is shown on top of
- // these sprites with priority 15 as well. This is undefined behavior
- // actually, as the sprites and dialog share the same priority, so in our
- // implementation the sprites get drawn incorrectly on top of the dialog.
- // Perhaps this worked by mistake in SSCI because of subtle differences in
- // how sprites are drawn. We compensate for this by resetting the priority
- // of all sprites that have a priority of 15 in scene 440 to priority 14,
- // so that the speech bubble can be drawn correctly on top of them. Fixes
- // bug #3040625.
- if (g_sci->getGameId() == GID_ECOQUEST && g_sci->getEngineState()->currentRoomNumber() == 440 && priority == 15)
- priority = 14;
-
if (!_EGAmapping) {
for (y = 0; y < height; y++, bitmap += celWidth) {
for (x = 0; x < width; x++) {
@@ -855,12 +843,11 @@ void GfxView::draw(const Common::Rect &rect, const Common::Rect &clipRect, const
const int y2 = clipRectTranslated.top + y;
if (!upscaledHires) {
if (priority >= _screen->getPriority(x2, y2)) {
- if (!_palette->isRemapped(palette->mapping[color])) {
- _screen->putPixel(x2, y2, drawMask, palette->mapping[color], priority, 0);
- } else {
- byte remappedColor = _palette->remapColor(palette->mapping[color], _screen->getVisual(x2, y2));
- _screen->putPixel(x2, y2, drawMask, remappedColor, priority, 0);
- }
+ byte outputColor = palette->mapping[color];
+ // SCI16 remapping (QFG4 demo)
+ if (g_sci->_gfxRemap16 && g_sci->_gfxRemap16->isRemapped(outputColor))
+ outputColor = g_sci->_gfxRemap16->remapColor(outputColor, _screen->getVisual(x2, y2));
+ _screen->putPixel(x2, y2, drawMask, outputColor, priority, 0);
}
} else {
// UpscaledHires means view is hires and is supposed to
@@ -970,12 +957,11 @@ void GfxView::drawScaled(const Common::Rect &rect, const Common::Rect &clipRect,
const int x2 = clipRectTranslated.left + x;
const int y2 = clipRectTranslated.top + y;
if (color != clearKey && priority >= _screen->getPriority(x2, y2)) {
- if (!_palette->isRemapped(palette->mapping[color])) {
- _screen->putPixel(x2, y2, drawMask, palette->mapping[color], priority, 0);
- } else {
- byte remappedColor = _palette->remapColor(palette->mapping[color], _screen->getVisual(x2, y2));
- _screen->putPixel(x2, y2, drawMask, remappedColor, priority, 0);
- }
+ byte outputColor = palette->mapping[color];
+ // SCI16 remapping (QFG4 demo)
+ if (g_sci->_gfxRemap16 && g_sci->_gfxRemap16->isRemapped(outputColor))
+ outputColor = g_sci->_gfxRemap16->remapColor(outputColor, _screen->getVisual(x2, y2));
+ _screen->putPixel(x2, y2, drawMask, outputColor, priority, 0);
}
}
}
@@ -989,13 +975,4 @@ void GfxView::adjustBackUpscaledCoordinates(int16 &y, int16 &x) {
_screen->adjustBackUpscaledCoordinates(y, x, _sci2ScaleRes);
}
-byte GfxView::getColorAtCoordinate(int16 loopNo, int16 celNo, int16 x, int16 y) {
- const CelInfo *celInfo = getCelInfo(loopNo, celNo);
- const byte *bitmap = getBitmap(loopNo, celNo);
- const int16 celWidth = celInfo->width;
-
- bitmap += (celWidth * y);
- return bitmap[x];
-}
-
} // End of namespace Sci
diff --git a/engines/sci/graphics/view.h b/engines/sci/graphics/view.h
index d8803db208..91590208c1 100644
--- a/engines/sci/graphics/view.h
+++ b/engines/sci/graphics/view.h
@@ -85,8 +85,6 @@ public:
void adjustToUpscaledCoordinates(int16 &y, int16 &x);
void adjustBackUpscaledCoordinates(int16 &y, int16 &x);
- byte getColorAtCoordinate(int16 loopNo, int16 celNo, int16 x, int16 y);
-
private:
void initData(GuiResourceId resourceId);
void unpackCel(int16 loopNo, int16 celNo, byte *outPtr, uint32 pixelCount);
diff --git a/engines/sci/module.mk b/engines/sci/module.mk
index 08e5ea84d8..a02147e4d0 100644
--- a/engines/sci/module.mk
+++ b/engines/sci/module.mk
@@ -57,6 +57,7 @@ MODULE_OBJS := \
graphics/picture.o \
graphics/portrait.o \
graphics/ports.o \
+ graphics/remap.o \
graphics/screen.o \
graphics/text16.o \
graphics/transitions.o \
@@ -81,10 +82,13 @@ MODULE_OBJS := \
ifdef ENABLE_SCI32
MODULE_OBJS += \
engine/kgraphics32.o \
+ graphics/celobj32.o \
graphics/controls32.o \
graphics/frameout.o \
graphics/paint32.o \
+ graphics/plane32.o \
graphics/palette32.o \
+ graphics/screen_item32.o \
graphics/text32.o \
video/robot_decoder.o
endif
diff --git a/engines/sci/parser/vocabulary.cpp b/engines/sci/parser/vocabulary.cpp
index 828a57abeb..a09ba8f3ce 100644
--- a/engines/sci/parser/vocabulary.cpp
+++ b/engines/sci/parser/vocabulary.cpp
@@ -121,7 +121,7 @@ bool Vocabulary::loadParserWords() {
}
}
- unsigned int seeker;
+ uint32 seeker;
if (resourceType == kVocabularySCI1)
seeker = 255 * 2; // vocab.900 starts with 255 16-bit pointers which we don't use
else
@@ -202,7 +202,7 @@ bool Vocabulary::loadSuffixes() {
if (!resource)
return false; // No vocabulary found
- unsigned int seeker = 1;
+ uint32 seeker = 1;
while ((seeker < resource->size - 1) && (resource->data[seeker + 1] != 0xff)) {
suffix_t suffix;
@@ -288,7 +288,7 @@ bool Vocabulary::loadAltInputs() {
AltInput t;
t._input = data;
- unsigned int l = strlen(data);
+ uint32 l = strlen(data);
t._inputLength = l;
data += l + 1;
@@ -325,15 +325,15 @@ bool Vocabulary::checkAltInput(Common::String& text, uint16& cursorPos) {
return false;
bool ret = false;
- unsigned int loopCount = 0;
+ uint32 loopCount = 0;
bool changed;
do {
changed = false;
const char* t = text.c_str();
- unsigned int tlen = text.size();
+ uint32 tlen = text.size();
- for (unsigned int p = 0; p < tlen && !changed; ++p) {
+ for (uint32 p = 0; p < tlen && !changed; ++p) {
unsigned char s = t[p];
if (s >= _altInputs.size() || _altInputs[s].empty())
continue;
@@ -351,7 +351,7 @@ bool Vocabulary::checkAltInput(Common::String& text, uint16& cursorPos) {
cursorPos = p + strlen(i->_replacement);
}
- for (unsigned int j = 0; j < i->_inputLength; ++j)
+ for (uint32 j = 0; j < i->_inputLength; ++j)
text.deleteChar(p);
const char *r = i->_replacement;
while (*r)
diff --git a/engines/sci/parser/vocabulary.h b/engines/sci/parser/vocabulary.h
index f4adee6e55..59558ce18a 100644
--- a/engines/sci/parser/vocabulary.h
+++ b/engines/sci/parser/vocabulary.h
@@ -156,7 +156,7 @@ typedef Common::Array<synonym_t> SynonymList;
struct AltInput {
const char *_input;
const char *_replacement;
- unsigned int _inputLength;
+ uint32 _inputLength;
bool _prefix;
};
diff --git a/engines/sci/resource.cpp b/engines/sci/resource.cpp
index 54ef4b3363..6a5af1a6d6 100644
--- a/engines/sci/resource.cpp
+++ b/engines/sci/resource.cpp
@@ -307,7 +307,7 @@ bool Resource::loadPatch(Common::SeekableReadStream *file) {
error("Can't allocate %d bytes needed for loading %s", res->size + res->_headerSize, res->_id.toString().c_str());
}
- unsigned int really_read;
+ uint32 really_read;
if (res->_headerSize > 0) {
really_read = file->read(res->_header, res->_headerSize);
if (really_read != res->_headerSize)
@@ -565,12 +565,11 @@ Resource *ResourceManager::testResource(ResourceId id) {
}
int ResourceManager::addAppropriateSources() {
- Common::ArchiveMemberList files;
-
if (Common::File::exists("resource.map")) {
// SCI0-SCI2 file naming scheme
ResourceSource *map = addExternalMap("resource.map");
+ Common::ArchiveMemberList files;
SearchMan.listMatchingMembers(files, "resource.0??");
for (Common::ArchiveMemberList::const_iterator x = files.begin(); x != files.end(); ++x) {
@@ -587,20 +586,20 @@ int ResourceManager::addAppropriateSources() {
#endif
} else if (Common::MacResManager::exists("Data1")) {
// Mac SCI1.1+ file naming scheme
- SearchMan.listMatchingMembers(files, "Data?*");
+ Common::StringArray files;
+ Common::MacResManager::listFiles(files, "Data?");
- for (Common::ArchiveMemberList::const_iterator x = files.begin(); x != files.end(); ++x) {
- Common::String filename = (*x)->getName();
- addSource(new MacResourceForkResourceSource(filename, atoi(filename.c_str() + 4)));
+ for (Common::StringArray::const_iterator x = files.begin(); x != files.end(); ++x) {
+ addSource(new MacResourceForkResourceSource(*x, atoi(x->c_str() + 4)));
}
#ifdef ENABLE_SCI32
// There can also be a "Patches" resource fork with patches
- if (Common::File::exists("Patches"))
+ if (Common::MacResManager::exists("Patches"))
addSource(new MacResourceForkResourceSource("Patches", 100));
} else {
// SCI2.1-SCI3 file naming scheme
- Common::ArchiveMemberList mapFiles;
+ Common::ArchiveMemberList mapFiles, files;
SearchMan.listMatchingMembers(mapFiles, "resmap.0??");
SearchMan.listMatchingMembers(files, "ressci.0??");
@@ -865,6 +864,7 @@ ResourceManager::ResourceManager() {
}
void ResourceManager::init() {
+ _maxMemoryLRU = 256 * 1024; // 256KiB
_memoryLocked = 0;
_memoryLRU = 0;
_LRU.clear();
@@ -918,6 +918,14 @@ void ResourceManager::init() {
debugC(1, kDebugLevelResMan, "resMan: Detected %s", getSciVersionDesc(getSciVersion()));
+ // Resources in SCI32 games are significantly larger than SCI16
+ // games and can cause immediate exhaustion of the LRU resource
+ // cache, leading to constant decompression of picture resources
+ // and making the renderer very slow.
+ if (getSciVersion() >= SCI_VERSION_2) {
+ _maxMemoryLRU = 2048 * 1024; // 2MiB
+ }
+
switch (_viewType) {
case kViewEga:
debugC(1, kDebugLevelResMan, "resMan: Detected EGA graphic resources");
@@ -935,35 +943,14 @@ void ResourceManager::init() {
debugC(1, kDebugLevelResMan, "resMan: Detected SCI1.1 VGA graphic resources");
break;
default:
-#ifdef ENABLE_SCI32
- error("resMan: Couldn't determine view type");
-#else
- if (getSciVersion() >= SCI_VERSION_2) {
- // SCI support isn't built in, thus the view type won't be determined for
- // SCI2+ games. This will be handled further up, so throw no error here
- } else {
- error("resMan: Couldn't determine view type");
- }
-#endif
+ // Throw a warning, but do not error out here, because this is called from the
+ // fallback detector, and the user could be pointing to a folder with a non-SCI
+ // game, but with SCI-like file names (e.g. Pinball Creep)
+ warning("resMan: Couldn't determine view type");
+ break;
}
}
-void ResourceManager::initForDetection() {
- assert(!g_sci);
-
- _memoryLocked = 0;
- _memoryLRU = 0;
- _LRU.clear();
- _resMap.clear();
- _audioMapSCI1 = NULL;
-
- _mapVersion = detectMapVersion();
- _volVersion = detectVolVersion();
-
- scanNewSources();
- detectSciVersion();
-}
-
ResourceManager::~ResourceManager() {
// freeing resources
ResourceMap::iterator itr = _resMap.begin();
@@ -998,9 +985,9 @@ void ResourceManager::addToLRU(Resource *res) {
_LRU.push_front(res);
_memoryLRU += res->size;
#if SCI_VERBOSE_RESMAN
- debug("Adding %s.%03d (%d bytes) to lru control: %d bytes total",
- getResourceTypeName(res->type), res->number, res->size,
- mgr->_memoryLRU);
+ debug("Adding %s (%d bytes) to lru control: %d bytes total",
+ res->_id.toString().c_str(), res->size,
+ _memoryLRU);
#endif
res->_status = kResStatusEnqueued;
}
@@ -1023,13 +1010,13 @@ void ResourceManager::printLRU() {
}
void ResourceManager::freeOldResources() {
- while (MAX_MEMORY < _memoryLRU) {
+ while (_maxMemoryLRU < _memoryLRU) {
assert(!_LRU.empty());
Resource *goner = *_LRU.reverse_begin();
removeFromLRU(goner);
goner->unalloc();
#ifdef SCI_VERBOSE_RESMAN
- debug("resMan-debug: LRU: Freeing %s.%03d (%d bytes)", getResourceTypeName(goner->type), goner->number, goner->size);
+ debug("resMan-debug: LRU: Freeing %s (%d bytes)", goner->_id.toString().c_str(), goner->size);
#endif
}
}
@@ -2474,7 +2461,9 @@ bool ResourceManager::hasOldScriptHeader() {
Resource *res = findResource(ResourceId(kResourceTypeScript, 0), 0);
if (!res) {
- error("resMan: Failed to find script.000");
+ // Script 0 missing -> corrupted / non-SCI resource files.
+ // Don't error out here, because this might have been called
+ // from the fallback detector
return false;
}
@@ -2679,7 +2668,9 @@ Common::String ResourceManager::findSierraGameId() {
return "";
// Seek to the name selector of the first export
- byte *seeker = heap->data + READ_UINT16(heap->data + gameObjectOffset + nameSelector * 2);
+ byte *offsetPtr = heap->data + gameObjectOffset + nameSelector * 2;
+ uint16 offset = !isSci11Mac() ? READ_LE_UINT16(offsetPtr) : READ_BE_UINT16(offsetPtr);
+ byte *seeker = heap->data + offset;
Common::String sierraId;
sierraId += (const char *)seeker;
diff --git a/engines/sci/resource.h b/engines/sci/resource.h
index eb5b508254..ef474d97c2 100644
--- a/engines/sci/resource.h
+++ b/engines/sci/resource.h
@@ -315,11 +315,6 @@ public:
void init();
/**
- * Similar to the function above, only called from the fallback detector
- */
- void initForDetection();
-
- /**
* Adds all of the resource files for a game
*/
int addAppropriateSources();
@@ -426,9 +421,7 @@ protected:
// Note: maxMemory will not be interpreted as a hard limit, only as a restriction
// for resources which are not explicitly locked. However, a warning will be
// issued whenever this limit is exceeded.
- enum {
- MAX_MEMORY = 256 * 1024 // 256KB
- };
+ int _maxMemoryLRU;
ViewType _viewType; // Used to determine if the game has EGA or VGA graphics
Common::List<ResourceSource *> _sources;
diff --git a/engines/sci/resource_audio.cpp b/engines/sci/resource_audio.cpp
index 6869e6379e..5717a09121 100644
--- a/engines/sci/resource_audio.cpp
+++ b/engines/sci/resource_audio.cpp
@@ -139,7 +139,7 @@ bool Resource::loadFromAudioVolumeSCI1(Common::SeekableReadStream *file) {
error("Can't allocate %d bytes needed for loading %s", size, _id.toString().c_str());
}
- unsigned int really_read = file->read(data, size);
+ uint32 really_read = file->read(data, size);
if (really_read != size)
warning("Read %d bytes from %s but expected %d", really_read, _id.toString().c_str(), size);
@@ -688,6 +688,12 @@ SoundResource::SoundResource(uint32 resourceNr, ResourceManager *resMan, SciVers
channel->data = resource->data + dataOffset;
channel->size = READ_LE_UINT16(data + 4);
+
+ if (dataOffset + channel->size > resource->size) {
+ warning("Invalid size inside sound resource %d: track %d, channel %d", resourceNr, trackNr, channelNr);
+ channel->size = resource->size - dataOffset;
+ }
+
channel->curPos = 0;
channel->number = *channel->data;
diff --git a/engines/sci/sci.cpp b/engines/sci/sci.cpp
index 1232b6559b..e14d12b918 100644
--- a/engines/sci/sci.cpp
+++ b/engines/sci/sci.cpp
@@ -58,6 +58,7 @@
#include "sci/graphics/picture.h"
#include "sci/graphics/ports.h"
#include "sci/graphics/palette.h"
+#include "sci/graphics/remap.h"
#include "sci/graphics/screen.h"
#include "sci/graphics/text16.h"
#include "sci/graphics/transitions.h"
@@ -163,6 +164,7 @@ SciEngine::~SciEngine() {
delete _gfxText32;
delete _robotDecoder;
delete _gfxFrameout;
+ delete _gfxRemap32;
#endif
delete _gfxMenu;
delete _gfxControls16;
@@ -175,6 +177,7 @@ SciEngine::~SciEngine() {
delete _gfxPorts;
delete _gfxCache;
delete _gfxPalette16;
+ delete _gfxRemap16;
delete _gfxCursor;
delete _gfxScreen;
@@ -238,13 +241,7 @@ Common::Error SciEngine::run() {
// Only DOS+Windows
switch (_gameId) {
case GID_KQ6:
- if (isCD())
- _forceHiresGraphics = ConfMan.getBool("enable_high_resolution_graphics");
- break;
case GID_GK1:
- if ((isCD()) && (!isDemo()))
- _forceHiresGraphics = ConfMan.getBool("enable_high_resolution_graphics");
- break;
case GID_PQ4:
if (isCD())
_forceHiresGraphics = ConfMan.getBool("enable_high_resolution_graphics");
@@ -316,6 +313,7 @@ Common::Error SciEngine::run() {
if (directSaveSlotLoading >= 0) {
_gamestate->_delayedRestoreGame = true;
_gamestate->_delayedRestoreGameId = directSaveSlotLoading;
+ _gamestate->_delayedRestoreFromLauncher = true;
// Jones only initializes its menus when restarting/restoring, thus set
// the gameIsRestarting flag here before initializing. Fixes bug #6536.
@@ -529,7 +527,7 @@ void SciEngine::patchGameSaveRestore() {
byte kernelIdSave = 0;
switch (_gameId) {
- case GID_HOYLE1: // gets confused, although the game doesnt support saving/restoring at all
+ case GID_HOYLE1: // gets confused, although the game doesn't support saving/restoring at all
case GID_HOYLE2: // gets confused, see hoyle1
case GID_JONES: // gets confused, when we patch us in, the game is only able to save to 1 slot, so hooking is not required
case GID_MOTHERGOOSE: // mother goose EGA saves/restores directly and has no save/restore dialogs
@@ -576,17 +574,29 @@ void SciEngine::patchGameSaveRestore() {
}
}
+ const Object *patchObjectSave = nullptr;
+
+ if (getSciVersion() < SCI_VERSION_2) {
+ // Patch gameobject ::save for now for SCI0 - SCI1.1
+ // TODO: It seems this was never adjusted to superclass, but adjusting it now may cause
+ // issues with some game. Needs to get checked and then possibly changed.
+ patchObjectSave = gameObject;
+ } else {
+ // Patch superclass ::save for SCI32
+ patchObjectSave = gameSuperObject;
+ }
+
// Search for gameobject ::save, if there is one patch that one too
- uint16 gameObjectMethodCount = gameObject->getMethodCount();
- for (uint16 methodNr = 0; methodNr < gameObjectMethodCount; methodNr++) {
- uint16 selectorId = gameObject->getFuncSelector(methodNr);
+ uint16 patchObjectMethodCount = patchObjectSave->getMethodCount();
+ for (uint16 methodNr = 0; methodNr < patchObjectMethodCount; methodNr++) {
+ uint16 selectorId = patchObjectSave->getFuncSelector(methodNr);
Common::String methodName = _kernel->getSelectorName(selectorId);
if (methodName == "save") {
if (_gameId != GID_FAIRYTALES) { // Fairy Tales saves automatically without a dialog
if (kernelIdSave != kernelIdRestore)
- patchGameSaveRestoreCode(segMan, gameObject->getFunction(methodNr), kernelIdSave);
+ patchGameSaveRestoreCode(segMan, patchObjectSave->getFunction(methodNr), kernelIdSave);
else
- patchGameSaveRestoreCodeSci21(segMan, gameObject->getFunction(methodNr), kernelIdSave, false);
+ patchGameSaveRestoreCodeSci21(segMan, patchObjectSave->getFunction(methodNr), kernelIdSave, false);
}
break;
}
@@ -653,6 +663,7 @@ void SciEngine::initGraphics() {
_gfxPaint = 0;
_gfxPaint16 = 0;
_gfxPalette16 = 0;
+ _gfxRemap16 = 0;
_gfxPorts = 0;
_gfxText16 = 0;
_gfxTransitions = 0;
@@ -663,6 +674,7 @@ void SciEngine::initGraphics() {
_gfxFrameout = 0;
_gfxPaint32 = 0;
_gfxPalette32 = 0;
+ _gfxRemap32 = 0;
#endif
if (hasMacIconBar())
@@ -672,9 +684,12 @@ void SciEngine::initGraphics() {
if (getSciVersion() >= SCI_VERSION_2) {
_gfxPalette32 = new GfxPalette32(_resMan, _gfxScreen);
_gfxPalette16 = _gfxPalette32;
+ _gfxRemap32 = new GfxRemap32(_gfxPalette32);
} else {
#endif
_gfxPalette16 = new GfxPalette(_resMan, _gfxScreen);
+ if (getGameId() == GID_QFG4DEMO)
+ _gfxRemap16 = new GfxRemap(_gfxPalette16);
#ifdef ENABLE_SCI32
}
#endif
@@ -690,10 +705,11 @@ void SciEngine::initGraphics() {
_gfxCompare = new GfxCompare(_gamestate->_segMan, _gfxCache, _gfxScreen, _gfxCoordAdjuster);
_gfxPaint32 = new GfxPaint32(_resMan, _gfxCoordAdjuster, _gfxScreen, _gfxPalette32);
_gfxPaint = _gfxPaint32;
- _gfxText32 = new GfxText32(_gamestate->_segMan, _gfxCache, _gfxScreen);
- _gfxControls32 = new GfxControls32(_gamestate->_segMan, _gfxCache, _gfxText32);
_robotDecoder = new RobotDecoder(getPlatform() == Common::kPlatformMacintosh);
_gfxFrameout = new GfxFrameout(_gamestate->_segMan, _resMan, _gfxCoordAdjuster, _gfxCache, _gfxScreen, _gfxPalette32, _gfxPaint32);
+ _gfxText32 = new GfxText32(_gamestate->_segMan, _gfxCache);
+ _gfxControls32 = new GfxControls32(_gamestate->_segMan, _gfxCache, _gfxText32);
+ _gfxFrameout->run();
} else {
#endif
// SCI0-SCI1.1 graphic objects creation
@@ -704,7 +720,7 @@ void SciEngine::initGraphics() {
_gfxTransitions = new GfxTransitions(_gfxScreen, _gfxPalette16);
_gfxPaint16 = new GfxPaint16(_resMan, _gamestate->_segMan, _gfxCache, _gfxPorts, _gfxCoordAdjuster, _gfxScreen, _gfxPalette16, _gfxTransitions, _audio);
_gfxPaint = _gfxPaint16;
- _gfxAnimate = new GfxAnimate(_gamestate, _gfxCache, _gfxPorts, _gfxPaint16, _gfxScreen, _gfxPalette16, _gfxCursor, _gfxTransitions);
+ _gfxAnimate = new GfxAnimate(_gamestate, _scriptPatcher, _gfxCache, _gfxPorts, _gfxPaint16, _gfxScreen, _gfxPalette16, _gfxCursor, _gfxTransitions);
_gfxText16 = new GfxText16(_gfxCache, _gfxPorts, _gfxPaint16, _gfxScreen);
_gfxControls16 = new GfxControls16(_gamestate->_segMan, _gfxPorts, _gfxPaint16, _gfxText16, _gfxScreen);
_gfxMenu = new GfxMenu(_eventMan, _gamestate->_segMan, _gfxPorts, _gfxPaint16, _gfxText16, _gfxScreen, _gfxCursor);
@@ -820,7 +836,7 @@ Console *SciEngine::getSciDebugger() {
}
const char *SciEngine::getGameIdStr() const {
- return _gameDescription->gameid;
+ return _gameDescription->gameId;
}
Common::Language SciEngine::getLanguage() const {
@@ -897,12 +913,30 @@ int SciEngine::inQfGImportRoom() const {
void SciEngine::setLauncherLanguage() {
if (_gameDescription->flags & ADGF_ADDENGLISH) {
// If game is multilingual
- if (Common::parseLanguage(ConfMan.get("language")) == Common::EN_ANY) {
+ Common::Language chosenLanguage = Common::parseLanguage(ConfMan.get("language"));
+ uint16 languageToSet = 0;
+
+ switch (chosenLanguage) {
+ case Common::EN_ANY:
// and English was selected as language
- if (SELECTOR(printLang) != -1) // set text language to English
- writeSelectorValue(_gamestate->_segMan, _gameObjectAddress, SELECTOR(printLang), K_LANG_ENGLISH);
- if (SELECTOR(parseLang) != -1) // and set parser language to English as well
- writeSelectorValue(_gamestate->_segMan, _gameObjectAddress, SELECTOR(parseLang), K_LANG_ENGLISH);
+ languageToSet = K_LANG_ENGLISH;
+ break;
+ case Common::JA_JPN: {
+ // Set Japanese for FM-Towns games
+ // KQ5 on FM-Towns has no initial language set
+ if (g_sci->getPlatform() == Common::kPlatformFMTowns) {
+ languageToSet = K_LANG_JAPANESE;
+ }
+ }
+ default:
+ break;
+ }
+
+ if (languageToSet) {
+ if (SELECTOR(printLang) != -1) // set text language
+ writeSelectorValue(_gamestate->_segMan, _gameObjectAddress, SELECTOR(printLang), languageToSet);
+ if (SELECTOR(parseLang) != -1) // and set parser language as well
+ writeSelectorValue(_gamestate->_segMan, _gameObjectAddress, SELECTOR(parseLang), languageToSet);
}
}
}
diff --git a/engines/sci/sci.h b/engines/sci/sci.h
index 5c86d92355..7df3d38163 100644
--- a/engines/sci/sci.h
+++ b/engines/sci/sci.h
@@ -71,6 +71,8 @@ class GfxPaint16;
class GfxPaint32;
class GfxPalette;
class GfxPalette32;
+class GfxRemap;
+class GfxRemap32;
class GfxPorts;
class GfxScreen;
class GfxText16;
@@ -128,6 +130,7 @@ enum SciGameId {
GID_FAIRYTALES,
GID_FREDDYPHARKAS,
GID_FUNSEEKER,
+ GID_GK1DEMO, // We have a separate ID for GK1 demo, because it's actually a completely different game (SCI1.1 vs SCI2/SCI2.1)
GID_GK1,
GID_GK2,
GID_HOYLE1,
@@ -165,12 +168,14 @@ enum SciGameId {
GID_PQ2,
GID_PQ3,
GID_PQ4,
+ GID_PQ4DEMO, // We have a separate ID for PQ4 demo, because it's actually a completely different game (SCI1.1 vs SCI2/SCI2.1)
GID_PQSWAT,
GID_QFG1,
GID_QFG1VGA,
GID_QFG2,
GID_QFG3,
GID_QFG4,
+ GID_QFG4DEMO, // We have a separate ID for QFG4 demo, because it's actually a completely different game (SCI1.1 vs SCI2/SCI2.1)
GID_RAMA,
GID_SHIVERS,
//GID_SHIVERS2, // Not SCI
@@ -201,8 +206,8 @@ enum SciVersion {
SCI_VERSION_1_LATE, // Dr. Brain 1, EcoQuest 1, Longbow, PQ3, SQ1, LSL5, KQ5 CD
SCI_VERSION_1_1, // Dr. Brain 2, EcoQuest 1 CD, EcoQuest 2, KQ6, QFG3, SQ4CD, XMAS 1992 and many more
SCI_VERSION_2, // GK1, PQ4 floppy, QFG4 floppy
- SCI_VERSION_2_1_EARLY, // GK2 demo, KQ7, LSL6 hires, PQ4, QFG4 floppy
- SCI_VERSION_2_1_MIDDLE, // GK2, KQ7, MUMG Deluxe, Phantasmagoria 1, PQ4CD, PQ:SWAT, QFG4CD, Shivers 1, SQ6, Torin
+ SCI_VERSION_2_1_EARLY, // GK2 demo, KQ7 1.4/1.51, LSL6 hires, PQ4CD, QFG4 floppy
+ SCI_VERSION_2_1_MIDDLE, // GK2, KQ7 2.00b, MUMG Deluxe, Phantasmagoria 1, PQ:SWAT, QFG4CD, Shivers 1, SQ6, Torin
SCI_VERSION_2_1_LATE, // demos of LSL7, Lighthouse, RAMA
SCI_VERSION_3 // LSL7, Lighthouse, RAMA, Phantasmagoria 2
};
@@ -282,7 +287,7 @@ public:
inline EngineState *getEngineState() const { return _gamestate; }
inline Vocabulary *getVocabulary() const { return _vocabulary; }
inline EventManager *getEventManager() const { return _eventMan; }
- inline reg_t getGameObject() const { return _gameObjectAddress; }
+ inline reg_t getGameObject() const { return _gameObjectAddress; } // Gets the game object VM address
Common::RandomSource &getRNG() { return _rng; }
@@ -349,6 +354,8 @@ public:
GfxMenu *_gfxMenu; // Menu for 16-bit gfx
GfxPalette *_gfxPalette16;
GfxPalette32 *_gfxPalette32; // Palette for 32-bit gfx
+ GfxRemap *_gfxRemap16; // Remapping for the QFG4 demo
+ GfxRemap32 *_gfxRemap32; // Remapping for 32-bit gfx
GfxPaint *_gfxPaint;
GfxPaint16 *_gfxPaint16; // Painting in 16-bit gfx
GfxPaint32 *_gfxPaint32; // Painting in 32-bit gfx
diff --git a/engines/sci/sound/drivers/amigamac.cpp b/engines/sci/sound/drivers/amigamac.cpp
index 5ce49086ca..0f93b19e7c 100644
--- a/engines/sci/sound/drivers/amigamac.cpp
+++ b/engines/sci/sound/drivers/amigamac.cpp
@@ -497,7 +497,7 @@ MidiDriver_AmigaMac::InstrumentSample *MidiDriver_AmigaMac::readInstrumentSCI0(C
}
instrument->samples = (int8 *) malloc(size + 1);
- if (file.read(instrument->samples, size) < (unsigned int)size) {
+ if (file.read(instrument->samples, size) < (uint32)size) {
warning("Amiga/Mac driver: failed to read instrument samples");
free(instrument->samples);
delete instrument;
diff --git a/engines/sci/sound/soundcmd.cpp b/engines/sci/sound/soundcmd.cpp
index ee5903fda2..e7b25eb1fc 100644
--- a/engines/sci/sound/soundcmd.cpp
+++ b/engines/sci/sound/soundcmd.cpp
@@ -44,7 +44,7 @@ SoundCommandParser::SoundCommandParser(ResourceManager *resMan, SegManager *segM
// resource number, but it's totally unrelated to the menu music).
// The GK1 demo (very late SCI1.1) does the same thing
// TODO: Check the QFG4 demo
- _useDigitalSFX = (getSciVersion() >= SCI_VERSION_2 || g_sci->getGameId() == GID_GK1 || ConfMan.getBool("prefer_digitalsfx"));
+ _useDigitalSFX = (getSciVersion() >= SCI_VERSION_2 || g_sci->getGameId() == GID_GK1DEMO || ConfMan.getBool("prefer_digitalsfx"));
_music = new SciMusic(_soundVersion, _useDigitalSFX);
_music->init();
diff --git a/engines/sci/util.cpp b/engines/sci/util.cpp
index c72d3beb19..ccec41a1ab 100644
--- a/engines/sci/util.cpp
+++ b/engines/sci/util.cpp
@@ -69,4 +69,13 @@ void WRITE_SCI11ENDIAN_UINT16(void *ptr, uint16 val) {
WRITE_LE_UINT16(ptr, val);
}
+#ifdef ENABLE_SCI32
+void WRITE_SCI11ENDIAN_UINT32(void *ptr, uint32 val) {
+ if (g_sci->getPlatform() == Common::kPlatformMacintosh && getSciVersion() >= SCI_VERSION_1_1)
+ WRITE_BE_UINT32(ptr, val);
+ else
+ WRITE_LE_UINT32(ptr, val);
+}
+#endif
+
} // End of namespace Sci
diff --git a/engines/sci/util.h b/engines/sci/util.h
index 378030939c..b0fee5151e 100644
--- a/engines/sci/util.h
+++ b/engines/sci/util.h
@@ -37,6 +37,9 @@ void WRITE_SCIENDIAN_UINT16(void *ptr, uint16 val);
uint16 READ_SCI11ENDIAN_UINT16(const void *ptr);
uint32 READ_SCI11ENDIAN_UINT32(const void *ptr);
void WRITE_SCI11ENDIAN_UINT16(void *ptr, uint16 val);
+#ifdef ENABLE_SCI32
+void WRITE_SCI11ENDIAN_UINT32(void *ptr, uint32 val);
+#endif
// Wrappers for reading integer values in resources that are
// LE in SCI1.1 Mac, but BE in SCI32 Mac
diff --git a/engines/scumm/actor.cpp b/engines/scumm/actor.cpp
index 0d7ea39ec2..3a69b5f03c 100644
--- a/engines/scumm/actor.cpp
+++ b/engines/scumm/actor.cpp
@@ -861,7 +861,7 @@ L2C36:;
stopActorMoving();
return;
}
- // 2C98: Yes, an exact copy of what just occured.. the original does this, so im doing it...
+ // 2C98: Yes, an exact copy of what just occurred.. the original does this, so im doing it...
// Just to keep me sane when going over it :)
if (A == 0xFF) {
setActorFromTmp();
diff --git a/engines/scumm/charset-fontdata.cpp b/engines/scumm/charset-fontdata.cpp
index 23e89b1878..a1e92a9950 100644
--- a/engines/scumm/charset-fontdata.cpp
+++ b/engines/scumm/charset-fontdata.cpp
@@ -591,35 +591,40 @@ CharsetRendererV2::CharsetRendererV2(ScummEngine *vm, Common::Language language)
_fontHeight = 8;
_curId = 0;
- const byte *replacementData = NULL;
+ const byte *replacementMap = NULL, *replacementData = NULL;
int replacementChars = 0;
switch (language) {
case Common::DE_DEU:
if (_vm->_game.version == 0) {
- replacementData = germanCharsetDataV0;
+ replacementMap = germanCharsetDataV0;
replacementChars = sizeof(germanCharsetDataV0) / 2;
} else {
- replacementData = germanCharsetDataV2;
+ replacementMap = germanCharsetDataV2;
replacementChars = sizeof(germanCharsetDataV2) / 2;
}
+ replacementData = specialCharsetData;
break;
case Common::FR_FRA:
- replacementData = frenchCharsetDataV2;
+ replacementMap = frenchCharsetDataV2;
replacementChars = sizeof(frenchCharsetDataV2) / 2;
+ replacementData = specialCharsetData;
break;
case Common::IT_ITA:
- replacementData = italianCharsetDataV2;
+ replacementMap = italianCharsetDataV2;
replacementChars = sizeof(italianCharsetDataV2) / 2;
+ replacementData = specialCharsetData;
break;
case Common::ES_ESP:
- replacementData = spanishCharsetDataV2;
+ replacementMap = spanishCharsetDataV2;
replacementChars = sizeof(spanishCharsetDataV2) / 2;
+ replacementData = specialCharsetData;
break;
case Common::RU_RUS:
if (((_vm->_game.id == GID_MANIAC) || (_vm->_game.id == GID_ZAK)) && (_vm->_game.version == 2)) {
- replacementData = russCharsetDataV2;
+ replacementMap = russCharsetDataV2;
replacementChars = sizeof(russCharsetDataV2) / 2;
+ replacementData = russianCharsetDataV2;
} else {
_fontPtr = russianCharsetDataV2;
}
@@ -629,20 +634,16 @@ CharsetRendererV2::CharsetRendererV2(ScummEngine *vm, Common::Language language)
break;
}
- if (replacementData) {
+ if (replacementMap && replacementData) {
_fontPtr = new byte[sizeof(englishCharsetDataV2)];
_deleteFontPtr = true;
memcpy(const_cast<byte *>(_fontPtr), englishCharsetDataV2, sizeof(englishCharsetDataV2));
for (int i = 0; i < replacementChars; i++) {
- int ch1 = replacementData[2 * i];
- int ch2 = replacementData[2 * i + 1];
+ int ch1 = replacementMap[2 * i];
+ int ch2 = replacementMap[2 * i + 1];
- if (((_vm->_game.id == GID_MANIAC) || (_vm->_game.id == GID_ZAK)) && (_vm->_game.version == 2)) {
- memcpy(const_cast<byte *>(_fontPtr) + 8 * ch1, russianCharsetDataV2 + 8 * ch2, 8);
- } else {
- memcpy(const_cast<byte *>(_fontPtr) + 8 * ch1, specialCharsetData + 8 * ch2, 8);
- }
+ memcpy(const_cast<byte *>(_fontPtr) + 8 * ch1, replacementData + 8 * ch2, 8);
}
} else
_deleteFontPtr = false;
diff --git a/engines/scumm/configure.engine b/engines/scumm/configure.engine
index e1de788061..e8962a371e 100644
--- a/engines/scumm/configure.engine
+++ b/engines/scumm/configure.engine
@@ -2,4 +2,4 @@
# 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 he "HE71+ games" yes "" "" "highres"
diff --git a/engines/scumm/detection.cpp b/engines/scumm/detection.cpp
index e5bbad15e6..9264a6443b 100644
--- a/engines/scumm/detection.cpp
+++ b/engines/scumm/detection.cpp
@@ -1271,7 +1271,6 @@ SaveStateList ScummMetaEngine::listSaves(const char *target) const {
pattern += ".s##";
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) {
@@ -1288,6 +1287,8 @@ SaveStateList ScummMetaEngine::listSaves(const char *target) const {
}
}
+ // Sort saves based on slot number.
+ Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator());
return saveList;
}
diff --git a/engines/scumm/he/sound_he.cpp b/engines/scumm/he/sound_he.cpp
index a78aff96f8..b806a9f3cc 100644
--- a/engines/scumm/he/sound_he.cpp
+++ b/engines/scumm/he/sound_he.cpp
@@ -636,7 +636,7 @@ void SoundHE::playHESound(int soundID, int heOffset, int heChannel, int heFlags)
if (heFlags & 1) {
_heChannel[heChannel].timer = 0;
} else {
- _heChannel[heChannel].timer = size * 1000 / rate;
+ _heChannel[heChannel].timer = size * 1000 / (rate * blockAlign);
}
_mixer->stopHandle(_heSoundChannels[heChannel]);
@@ -658,7 +658,7 @@ void SoundHE::playHESound(int soundID, int heOffset, int heChannel, int heFlags)
_heChannel[heChannel].rate = rate;
if (_heChannel[heChannel].timer)
- _heChannel[heChannel].timer = size * 1000 / rate;
+ _heChannel[heChannel].timer = size * 1000 / (rate * blockAlign);
// makeADPCMStream returns a stream in native endianness, but RawMemoryStream
// defaults to big endian. If we're on a little endian system, set the LE flag.
diff --git a/engines/scumm/imuse_digi/dimuse_track.cpp b/engines/scumm/imuse_digi/dimuse_track.cpp
index b7abdd074e..28ad64670c 100644
--- a/engines/scumm/imuse_digi/dimuse_track.cpp
+++ b/engines/scumm/imuse_digi/dimuse_track.cpp
@@ -352,9 +352,9 @@ Track *IMuseDigital::cloneToFadeOutTrack(Track *track, int fadeDelay) {
// leaving bug number for now #1635361
ImuseDigiSndMgr::SoundDesc *soundDesc = _sound->cloneSound(track->soundDesc);
if (!soundDesc) {
- // it fail load open old song after switch to diffrent CDs
+ // it fail load open old song after switch to different CDs
// so gave up
- error("Game not supported while playing on 2 diffrent CDs");
+ error("Game not supported while playing on 2 different CDs");
}
track->soundDesc = soundDesc;
diff --git a/engines/scumm/players/player_ad.cpp b/engines/scumm/players/player_ad.cpp
index 4d4be2c3c2..55bbeeef98 100644
--- a/engines/scumm/players/player_ad.cpp
+++ b/engines/scumm/players/player_ad.cpp
@@ -50,7 +50,7 @@ Player_AD::Player_AD(ScummEngine *scumm)
writeReg(0x01, 0x20);
_engineMusicTimer = 0;
- _soundPlaying = -1;
+ _musicResource = -1;
_curOffset = 0;
@@ -104,8 +104,8 @@ void Player_AD::startSound(int sound) {
stopMusic();
// Lock the new music resource
- _soundPlaying = sound;
- _vm->_res->lock(rtSound, _soundPlaying);
+ _musicResource = sound;
+ _vm->_res->lock(rtSound, _musicResource);
// Start the new music resource
_musicData = res;
@@ -150,7 +150,7 @@ void Player_AD::startSound(int sound) {
void Player_AD::stopSound(int sound) {
Common::StackLock lock(_mutex);
- if (sound == _soundPlaying) {
+ if (sound == _musicResource) {
stopMusic();
} else {
for (int i = 0; i < ARRAYSIZE(_sfx); ++i) {
@@ -178,7 +178,17 @@ int Player_AD::getMusicTimer() {
}
int Player_AD::getSoundStatus(int sound) const {
- return (sound == _soundPlaying);
+ if (sound == _musicResource) {
+ return true;
+ }
+
+ for (int i = 0; i < ARRAYSIZE(_sfx); ++i) {
+ if (_sfx[i].resource == sound) {
+ return true;
+ }
+ }
+
+ return false;
}
void Player_AD::saveLoadWithSerializer(Serializer *ser) {
@@ -193,7 +203,7 @@ void Player_AD::saveLoadWithSerializer(Serializer *ser) {
if (ser->getVersion() >= VER(96)) {
int32 res[4] = {
- _soundPlaying, _sfx[0].resource, _sfx[1].resource, _sfx[2].resource
+ _musicResource, _sfx[0].resource, _sfx[1].resource, _sfx[2].resource
};
// The first thing we save is a list of sound resources being played
@@ -461,13 +471,13 @@ void Player_AD::startMusic() {
}
void Player_AD::stopMusic() {
- if (_soundPlaying == -1) {
+ if (_musicResource == -1) {
return;
}
// Unlock the music resource if present
- _vm->_res->unlock(rtSound, _soundPlaying);
- _soundPlaying = -1;
+ _vm->_res->unlock(rtSound, _musicResource);
+ _musicResource = -1;
// Stop the music playback
_curOffset = 0;
@@ -510,7 +520,7 @@ void Player_AD::updateMusic() {
// important to note that we need to parse a command directly
// at the new position, i.e. there is no time value we need to
// parse.
- if (_soundPlaying == -1) {
+ if (_musicResource == -1) {
return;
} else {
continue;
diff --git a/engines/scumm/players/player_ad.h b/engines/scumm/players/player_ad.h
index 63fda3cc7c..9cd1a06261 100644
--- a/engines/scumm/players/player_ad.h
+++ b/engines/scumm/players/player_ad.h
@@ -68,7 +68,7 @@ private:
OPL::OPL *_opl2;
- int _soundPlaying;
+ int _musicResource;
int32 _engineMusicTimer;
struct SfxSlot;
diff --git a/engines/scumm/players/player_v4a.cpp b/engines/scumm/players/player_v4a.cpp
index bd8ce3f7ad..58f53a6d7f 100644
--- a/engines/scumm/players/player_v4a.cpp
+++ b/engines/scumm/players/player_v4a.cpp
@@ -174,7 +174,7 @@ int Player_V4A::getMusicTimer() {
return 2000;
if (_musicId) {
// The titlesong (and a few others) is running with ~70 ticks per second and the scale seems to be based on that.
- // The Game itself doesnt get the timing from the Tfmx Player however, so we just use the elapsed time
+ // The Game itself doesn't get the timing from the Tfmx Player however, so we just use the elapsed time
// 357 ~ 1000 * 25 * (1 / 70)
return _mixer->getSoundElapsedTime(_musicHandle) / 357;
}
@@ -183,7 +183,7 @@ int Player_V4A::getMusicTimer() {
int Player_V4A::getSoundStatus(int nr) const {
// For music the game queues a variable the Tfmx Player sets through a special command.
- // For sfx there seems to be no way to queue them, and the game doesnt try to.
+ // For sfx there seems to be no way to queue them, and the game doesn't try to.
return (nr == _musicId) ? _signal : 0;
}
diff --git a/engines/scumm/saveload.cpp b/engines/scumm/saveload.cpp
index e5673c1803..f3df24ff51 100644
--- a/engines/scumm/saveload.cpp
+++ b/engines/scumm/saveload.cpp
@@ -1303,7 +1303,7 @@ void ScummEngine::saveOrLoad(Serializer *s) {
if (hasTownsData) {
// Skip FM-Towns specific data
- for (int i = 69 * sizeof(uint8) + 44 * sizeof(int16); i; i--)
+ for (i = 69 * sizeof(uint8) + 44 * sizeof(int16); i; i--)
s->loadByte();
}
diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp
index 24d676a1ff..89d2d3dc72 100644
--- a/engines/scumm/scumm.cpp
+++ b/engines/scumm/scumm.cpp
@@ -2611,8 +2611,12 @@ bool ScummEngine::startManiac() {
Common::String path = dom.getVal("path");
if (path.hasPrefix(currentPath)) {
- path.erase(0, currentPath.size() + 1);
- if (path.equalsIgnoreCase("maniac")) {
+ path.erase(0, currentPath.size());
+ // Do a case-insensitive non-path-mode match of the remainder.
+ // While strictly speaking it's too broad, this matchString
+ // ignores the presence or absence of trailing path separators
+ // in either currentPath or path.
+ if (path.matchString("*maniac*", true, false)) {
maniacTarget = iter->_key;
break;
}
diff --git a/engines/scumm/scumm_v4.h b/engines/scumm/scumm_v4.h
index 28f619ceaa..a008023ff9 100644
--- a/engines/scumm/scumm_v4.h
+++ b/engines/scumm/scumm_v4.h
@@ -35,7 +35,7 @@ class ScummEngine_v4 : public ScummEngine_v5 {
public:
/**
- * Prepared savegame used by the orginal save/load dialog.
+ * Prepared savegame used by the original save/load dialog.
* Must be valid as long as the savescreen is active. As we are not
* notified when the savescreen is closed, memory is only freed on a game
* reset, at the destruction of the engine or when the original save/load
diff --git a/engines/scumm/sound.cpp b/engines/scumm/sound.cpp
index 84d2b37f96..4d70ee8482 100644
--- a/engines/scumm/sound.cpp
+++ b/engines/scumm/sound.cpp
@@ -240,7 +240,7 @@ void Sound::playSound(int soundID) {
// mentioned in the bug report above; in case it is, I put a check here.
assert(soundID == 39);
- // The samplerate is copied from the sound resouce 39 of the PC CD/VGA
+ // The samplerate is copied from the sound resource 39 of the PC CD/VGA
// version of Monkey Island.
// Read info from the header
diff --git a/engines/sherlock/POTFILES b/engines/sherlock/POTFILES
new file mode 100644
index 0000000000..b9f1c1249f
--- /dev/null
+++ b/engines/sherlock/POTFILES
@@ -0,0 +1,3 @@
+engines/sherlock/detection.cpp
+engines/sherlock/scalpel/scalpel.cpp
+engines/sherlock/tattoo/widget_files.cpp
diff --git a/engines/sherlock/animation.cpp b/engines/sherlock/animation.cpp
index 681e71d0f6..4442c1da85 100644
--- a/engines/sherlock/animation.cpp
+++ b/engines/sherlock/animation.cpp
@@ -23,6 +23,8 @@
#include "sherlock/animation.h"
#include "sherlock/sherlock.h"
#include "sherlock/scalpel/scalpel_screen.h"
+#include "sherlock/scalpel/3do/scalpel_3do_screen.h"
+
#include "common/algorithm.h"
namespace Sherlock {
@@ -89,7 +91,7 @@ bool Animation::play(const Common::String &filename, bool intro, int minDelay, i
// Draw the sprite. Note that we explicitly use the raw frame below, rather than the ImageFrame,
// since we don't want the offsets in the image file to be used, just the explicit position we specify
- screen.transBlitFrom(images[imageFrame]._frame, pt);
+ screen.SHtransBlitFrom(images[imageFrame]._frame, pt);
} else {
// At this point, either the sprites for the frame has been complete, or there weren't any sprites
// at all to draw for the frame
@@ -201,7 +203,7 @@ bool Animation::play3DO(const Common::String &filename, bool intro, int minDelay
// Draw the sprite. Note that we explicitly use the raw frame below, rather than the ImageFrame,
// since we don't want the offsets in the image file to be used, just the explicit position we specify
- screen._backBuffer1.transBlitFrom(images[imageFrame]._frame, pt);
+ screen._backBuffer1.SHtransBlitFrom(images[imageFrame]._frame, pt);
if (!fadeActive)
screen.slamArea(pt.x, pt.y, images[imageFrame]._frame.w, images[imageFrame]._frame.h);
} else {
diff --git a/engines/sherlock/events.cpp b/engines/sherlock/events.cpp
index 4b0b7dfb3f..6cfee5d822 100644
--- a/engines/sherlock/events.cpp
+++ b/engines/sherlock/events.cpp
@@ -143,7 +143,7 @@ void Events::setCursor(CursorId cursorId, const Common::Point &cursorPos, const
// Form a single surface containing both frames
Surface s(r.width(), r.height());
- s.fill(TRANSPARENCY);
+ s.clear(TRANSPARENCY);
// Draw the passed image
Common::Point drawPos;
@@ -151,11 +151,11 @@ void Events::setCursor(CursorId cursorId, const Common::Point &cursorPos, const
drawPos.x = -cursorPt.x;
if (cursorPt.y < 0)
drawPos.y = -cursorPt.y;
- s.blitFrom(surface, Common::Point(drawPos.x, drawPos.y));
+ s.SHblitFrom(surface, Common::Point(drawPos.x, drawPos.y));
// Draw the cursor image
drawPos = Common::Point(MAX(cursorPt.x, (int16)0), MAX(cursorPt.y, (int16)0));
- s.transBlitFrom(cursorImg, Common::Point(drawPos.x, drawPos.y));
+ s.SHtransBlitFrom(cursorImg, Common::Point(drawPos.x, drawPos.y));
// Set up hotspot position for cursor, adjusting for cursor image's position within the surface
Common::Point hotspot;
@@ -163,7 +163,7 @@ void Events::setCursor(CursorId cursorId, const Common::Point &cursorPos, const
hotspot = Common::Point(8, 8);
hotspot += drawPos;
// Set the cursor
- setCursor(s.getRawSurface(), hotspot.x, hotspot.y);
+ setCursor(s, hotspot.x, hotspot.y);
}
void Events::animateCursorIfNeeded() {
diff --git a/engines/sherlock/fixed_text.cpp b/engines/sherlock/fixed_text.cpp
index cbee944120..4679fe58b8 100644
--- a/engines/sherlock/fixed_text.cpp
+++ b/engines/sherlock/fixed_text.cpp
@@ -27,6 +27,183 @@
namespace Sherlock {
+static const char *const fixedJournalTextEN[] = {
+ // Holmes asked/said...
+ "Holmes asked me, ",
+ "Holmes asked the Inspector, ",
+ "Holmes asked %s, ",
+ "Holmes said to me, ",
+ "Holmes said to the Inspector, ",
+ "Holmes said to %s, ",
+ // I asked/said...
+ "I replied, ",
+ "The reply was, ",
+ // Holmes/I/The Inspector/Person asked/said (without "Then" prefix)
+ "Holmes asked, ",
+ "Holmes said, ",
+ "I asked, ",
+ "I said, ",
+ "The Inspector asked, ",
+ "The Inspector said, ",
+ "%s asked, ",
+ "%s said, ",
+ // Then Holmes/I/The Inspector/Person asked/said
+ "Then Holmes asked, ",
+ "Then Holmes said, ",
+ "Then I asked, ",
+ "Then I said, ",
+ "Then the Inspector asked, ",
+ "Then the Inspector said, ",
+ "Then %s asked, ",
+ "Then %s said, "
+};
+
+static const char *const fixedJournalTextDE[] = {
+ // Holmes asked/said...
+ "Holmes fragte mich, ",
+ "Holmes fragte Inspektor Lestrade, ",
+ "Holmes fragte %s, ",
+ "Holmes sagte mir, ",
+ "Holmes sagte Inspektor Lestrade, ",
+ "Holmes sagte %s, ",
+ // I asked/said...
+ "Ich antwortete, ",
+ "Die Antwort lautete, ",
+ // Holmes/I/The Inspector/Person asked/said (without "Then" prefix)
+ "Holmes fragte, ", // original: "fragte Holmes"
+ "Holmes sagte, ", // original: "sagte Holmes"
+ "Ich fragte, ", // original: "fragte Ich"
+ "Ich sagte, ", // original: "sagte Ich"
+ "Der Inspektor fragte, ",
+ "Der Inspektor sagte, ",
+ "%s fragte, ",
+ "%s sagte, ",
+ // Then Holmes/I/The Inspector/Person asked/said
+ "Dann fragte Holmes, ",
+ "Dann sagte Holmes, ",
+ "Dann fragte ich, ", // original: "Dann sagte Ich"
+ "Dann sagte ich, ", // original: "Dann sagte Ich"
+ "Dann fragte der Inspektor, ",
+ "Dann sagte der Inspektor, ",
+ "Dann fragte %s, ",
+ "Dann sagte %s, "
+};
+
+// Only used for Sherlock Holmes 2, so special characters should use the SH2 charset
+// small a w/ accent grave: 0x85 / octal 205
+// small e w/ accent acute: 0x82 / octal 202
+// small e w/ accent grave: 0x8A / octal 212
+// small e w/ circonflexe: 0x88 / octal 210
+// small cedilla: 0x87 / octal 207
+static const char *const fixedJournalTextFR[] = {
+ // Holmes asked/said...
+ "Holmes me demanda, ", // original: "Holmes m'a demand\202, "
+ "Holmes demanda \205 l'inspecteur, ", // original: "Holmes a demand\202 \205 l'inspecteur, "
+ "Holmes demanda \205 %s, ", // original: "Holmes a demand\202 \205 %s, "
+ "Holmes me dit, ", // original: "Holmes m'a dit, "
+ "Holmes dit \205 l'inspecteur, ", // original: "Holmes a dit \205 l'inspecteur, "
+ "Holmes dit \205 %s, ", // original: "Holmes a dit \205 %s, "
+ // I asked/said...
+ "Je r\202pondis, ", // original: "J'ai r\202pondu, ",
+ "La r\202ponse fut, ",
+ // Holmes/I/The Inspector/Person asked/said (without "Then" prefix)
+ "Holmes demanda, ", // original: "Holmes a demand\202, "
+ "Holmes dit, ",
+ "Je demandai, ", // original: "J'ai demand\202, "
+ "Je dis, ", // original: "J'ai dit, "
+ "L'inspecteur demanda, ", // original: ""L'inspecteur a demand\202, "
+ "L'inspecteur dit, ",
+ "%s demanda, ", // original: "%s a demand\202, "
+ "%s dit, ",
+ // Then Holmes/I/The Inspector/Person asked/said
+ "Alors Holmes demanda, ", // original: it seems "puis"/"then" was not used/removed. They instead added a space character, so sentences looked weird
+ "Alors Holmes dit, ",
+ "Alors je demandai, ",
+ "Alors je dis, ",
+ "Alors l'inspecteur demanda, ",
+ "Alors l'inspecteur dit, ",
+ "Alors %s demanda, ",
+ "Alors %s dit, "
+};
+
+// Sherlock Holmes 1+2:
+// small e w/ accent bottom to top : 0x82 / octal 202
+// big E w/ accent bottom to top : 0x90 / octal 220
+// small a w/ accent bottom to top : 0xA0 / octal 240
+// small i w/ accent bottom to top : 0xA1 / octal 241
+// small o w/ accent bottom to top : 0xA2 / octal 242
+// small u w/ accent bottom to top : 0xA3 / octal 243
+// small n w/ wavy line : 0xA4 / octal 244
+// big N w/ wavy line : 0xA5 / octal 245
+// small a w/ under line : 0xA6 / octal 246
+// small o w/ under line : 0xA7 / octal 247
+// inverted question mark : 0xA8 / octal 250
+static const char *const fixedJournalTextES[] = {
+ // Holmes asked/said...
+ "Holmes me pregunt\242, ", // original: "Holmes me pidi\242, ",
+ "Holmes pregunt\242 al Inspector, ", // original: "el inspector"?
+ "Holmes pregunt\242 %s, ",
+ "Holmes me dijo, ",
+ "Holmes dijo al Inspector, ", // original: "el inspector"?
+ "Holmes dijo a %s, ", // original: "Holmes dijo a %s, "
+ // I asked/said...
+ "Yo content\202, ", // original: "Yo respond\241, ",
+ "La respuesta fue, ",
+ // Holmes/I/The Inspector/Person asked/said (without "Then" prefix)
+ "Holmes pregunt\242, ",
+ "Holmes dijo, ",
+ "Yo pregunt\202, ", // original: "Yo pregunt\242, ",
+ "Yo dije, ",
+ "El Inspector pregunt\242, ",
+ "El Inspector dijo, ",
+ "%s pregunt\242, ",
+ "%s dijo, ",
+ // Then Holmes/I/The Inspector/Person asked/said
+ "Despu\202s Holmes pregunt\242, ", // original: added "Entonces" instead of "Despues"
+ "Despu\202s Holmes dijo, ",
+ "Despu\202s yo pregunt\202, ", // "pregunt\242, "
+ "Despu\202s yo dije, ",
+ "Despu\202s el Inspector pregunt\242, ",
+ "Despu\202s el Inspector dijo, ",
+ "Despu\202s %s pregunt\242, ",
+ "Despu\202s %s dijo, "
+};
+
+FixedText::FixedText(SherlockEngine *vm) {
+ _vm = vm;
+
+ // Figure out which fixed texts to use
+ Common::Language curLanguage = _vm->getLanguage();
+
+ switch (curLanguage) {
+ case Common::EN_ANY:
+ // Used by Sherlock Holmes 1+2
+ _fixedJournalTextArray = fixedJournalTextEN;
+ _fixedObjectPickedUpText = "Picked up %s";
+ break;
+ case Common::DE_DEU:
+ // Used by Sherlock Holmes 1+2
+ _fixedJournalTextArray = fixedJournalTextDE;
+ _fixedObjectPickedUpText = "%s eingesteckt";
+ break;
+ case Common::FR_FRA:
+ // Used by Sherlock Holmes 2
+ _fixedJournalTextArray = fixedJournalTextFR;
+ _fixedObjectPickedUpText = ""; // Not used, because there is no French Sherlock Holmes 1
+ break;
+ case Common::ES_ESP:
+ // Used by Sherlock Holmes 1+2
+ _fixedJournalTextArray = fixedJournalTextES;
+ _fixedObjectPickedUpText = "Cogido/a %s";
+ break;
+ default:
+ // Default to English
+ _fixedJournalTextArray = fixedJournalTextEN;
+ _fixedObjectPickedUpText = "Picked up %s";
+ break;
+ }
+}
+
FixedText *FixedText::init(SherlockEngine *vm) {
if (vm->getGameID() == GType_SerratedScalpel)
return new Scalpel::ScalpelFixedText(vm);
@@ -34,5 +211,12 @@ FixedText *FixedText::init(SherlockEngine *vm) {
return new Tattoo::TattooFixedText(vm);
}
+const char *FixedText::getJournalText(int fixedJournalTextId) {
+ return _fixedJournalTextArray[fixedJournalTextId];
+}
+
+const char *FixedText::getObjectPickedUpText() {
+ return _fixedObjectPickedUpText;
+}
} // End of namespace Sherlock
diff --git a/engines/sherlock/fixed_text.h b/engines/sherlock/fixed_text.h
index 40444f4052..3eae60e1c3 100644
--- a/engines/sherlock/fixed_text.h
+++ b/engines/sherlock/fixed_text.h
@@ -39,13 +39,44 @@ enum FixedTextActionId {
kFixedTextAction_Use
};
+enum FixedJournalTextId {
+ // Holmes asked/said...
+ kFixedJournalText_HolmesAskedMe = 0,
+ kFixedJournalText_HolmesAskedTheInspector,
+ kFixedJournalText_HolmesAskedPerson,
+ kFixedJournalText_HolmesSaidToMe,
+ kFixedJournalText_HolmesSaidToTheInspector,
+ kFixedJournalText_HolmesSaidToPerson,
+ // I asked/said
+ kFixedJournalText_IReplied,
+ kFixedJournalText_TheReplyWas,
+ // Holmes/I/The Inspector/Person asked/said (without "Then" prefix)
+ kFixedJournalText_HolmesAsked,
+ kFixedJournalText_HolmesSaid,
+ kFixedJournalText_IAsked,
+ kFixedJournalText_ISaid,
+ kFixedJournalText_TheInspectorAsked,
+ kFixedJournalText_TheInspectorSaid,
+ kFixedJournalText_PersonAsked,
+ kFixedJournalText_PersonSaid,
+ // Then Holmes/I/The Inspector/Person asked/said
+ kFixedJournalText_ThenHolmesAsked,
+ kFixedJournalText_ThenHolmesSaid,
+ kFixedJournalText_ThenIAsked,
+ kFixedJournalText_ThenISaid,
+ kFixedJournalText_ThenTheInspectorAsked,
+ kFixedJournalText_ThenTheInspectorSaid,
+ kFixedJournalText_ThenPersonAsked,
+ kFixedJournalText_ThenPersonSaid
+};
+
class SherlockEngine;
class FixedText {
protected:
SherlockEngine *_vm;
- FixedText(SherlockEngine *vm) : _vm(vm) {}
+ FixedText(SherlockEngine *vm);
public:
static FixedText *init(SherlockEngine *vm);
virtual ~FixedText() {}
@@ -59,6 +90,20 @@ public:
* Get action message
*/
virtual const Common::String getActionMessage(FixedTextActionId actionId, int messageIndex) = 0;
+
+ /**
+ * Gets journal text
+ */
+ const char *getJournalText(int fixedJournalTextId);
+
+ /**
+ * Gets object "Picked Up" text
+ */
+ const char *getObjectPickedUpText();
+
+private:
+ const char *const *_fixedJournalTextArray;
+ const char *_fixedObjectPickedUpText;
};
} // End of namespace Sherlock
diff --git a/engines/sherlock/fonts.cpp b/engines/sherlock/fonts.cpp
index 8e36c3908a..5a14881f1c 100644
--- a/engines/sherlock/fonts.cpp
+++ b/engines/sherlock/fonts.cpp
@@ -43,7 +43,7 @@ void Fonts::setVm(SherlockEngine *vm) {
_charCount = 0;
}
-void Fonts::free() {
+void Fonts::freeFont() {
delete _font;
}
@@ -213,7 +213,7 @@ void Fonts::writeString(Surface *surface, const Common::String &str,
if (curChar < _charCount) {
ImageFrame &frame = (*_font)[curChar];
- surface->transBlitFrom(frame, Common::Point(charPos.x, charPos.y + _yOffsets[curChar]), false, overrideColor);
+ surface->SHtransBlitFrom(frame, Common::Point(charPos.x, charPos.y + _yOffsets[curChar]), false, overrideColor);
charPos.x += frame._frame.w + 1;
} else {
warning("Invalid character encountered - %d", (int)curChar);
diff --git a/engines/sherlock/fonts.h b/engines/sherlock/fonts.h
index a527cc73c0..3594d466c2 100644
--- a/engines/sherlock/fonts.h
+++ b/engines/sherlock/fonts.h
@@ -57,7 +57,7 @@ public:
/**
* Frees the font manager
*/
- static void free();
+ static void freeFont();
/**
* Set the font to use for writing text on the screen
diff --git a/engines/sherlock/image_file.h b/engines/sherlock/image_file.h
index da260ab30b..778332b726 100644
--- a/engines/sherlock/image_file.h
+++ b/engines/sherlock/image_file.h
@@ -46,6 +46,11 @@ struct ImageFrame {
Graphics::Surface _frame;
/**
+ * Converts an ImageFrame record to a surface for convenience in passing to drawing methods
+ */
+ operator const Graphics::Surface &() { return _frame; }
+
+ /**
* Decompress a single frame for the sprite
*/
void decompressFrame(const byte *src, bool isRoseTattoo);
diff --git a/engines/sherlock/journal.cpp b/engines/sherlock/journal.cpp
index a30734c4f5..8763af31dd 100644
--- a/engines/sherlock/journal.cpp
+++ b/engines/sherlock/journal.cpp
@@ -299,6 +299,7 @@ bool Journal::drawJournal(int direction, int howFar) {
}
void Journal::loadJournalFile(bool alreadyLoaded) {
+ FixedText &fixedText = *_vm->_fixedText;
People &people = *_vm->_people;
Screen &screen = *_vm->_screen;
Talk &talk = *_vm->_talk;
@@ -381,20 +382,34 @@ void Journal::loadJournalFile(bool alreadyLoaded) {
// If Holmes has something to say first, then take care of it
if (!replyOnly) {
// Handle the grammar
- journalString += "Holmes ";
+ bool asked = false;
+
if (talk[journalEntry._statementNum]._statement.hasSuffix("?"))
- journalString += "asked ";
- else
- journalString += "said to ";
+ asked = true;
if (talk._talkTo == 1) {
- journalString += "me";
+ if (asked)
+ journalString += fixedText.getJournalText(kFixedJournalText_HolmesAskedMe);
+ else
+ journalString += fixedText.getJournalText(kFixedJournalText_HolmesSaidToTheInspector);
+
} else if ((talk._talkTo == 2 && IS_SERRATED_SCALPEL) || (talk._talkTo == 18 && IS_ROSE_TATTOO)) {
- journalString += "the Inspector";
+ if (asked)
+ journalString += fixedText.getJournalText(kFixedJournalText_HolmesAskedTheInspector);
+ else
+ journalString += fixedText.getJournalText(kFixedJournalText_HolmesSaidToMe);
+
} else {
- journalString += people._characters[talk._talkTo]._name;
+ const char *text = nullptr;
+ if (asked)
+ text = fixedText.getJournalText(kFixedJournalText_HolmesAskedPerson);
+ else
+ text = fixedText.getJournalText(kFixedJournalText_HolmesSaidToPerson);
+
+ journalString += Common::String::format(text, people._characters[talk._talkTo]._name);
}
- journalString += ", \"";
+
+ journalString += "\"";
// Add the statement
journalString += statement._statement;
@@ -457,29 +472,46 @@ void Journal::loadJournalFile(bool alreadyLoaded) {
journalString += "\"\n";
if (talk._talkTo == 1)
- journalString += "I replied, \"";
+ journalString += fixedText.getJournalText(kFixedJournalText_IReplied);
else
- journalString += "The reply was, \"";
+ journalString += fixedText.getJournalText(kFixedJournalText_TheReplyWas);
} else {
- if (talk._talkTo == 1)
- journalString += "I";
- else if (talk._talkTo == inspectorId)
- journalString += "The Inspector";
- else
- journalString += people._characters[talk._talkTo]._name;
-
const byte *strP = replyP + 1;
byte v;
do {
v = *strP++;
} while (v && (v < opcodes[0]) && (v != '.') && (v != '!') && (v != '?'));
+ bool asked = false;
+
if (v == '?')
- journalString += " asked, \"";
- else
- journalString += " said, \"";
+ asked = true;
+
+ if (talk._talkTo == 1) {
+ if (asked)
+ journalString += fixedText.getJournalText(kFixedJournalText_IAsked);
+ else
+ journalString += fixedText.getJournalText(kFixedJournalText_ISaid);
+
+ } else if (talk._talkTo == inspectorId) {
+ if (asked)
+ journalString += fixedText.getJournalText(kFixedJournalText_TheInspectorAsked);
+ else
+ journalString += fixedText.getJournalText(kFixedJournalText_TheInspectorSaid);
+
+ } else {
+ const char *text = nullptr;
+ if (asked)
+ text = fixedText.getJournalText(kFixedJournalText_PersonAsked);
+ else
+ text = fixedText.getJournalText(kFixedJournalText_PersonSaid);
+
+ journalString += Common::String::format(text, people._characters[talk._talkTo]._name);
+ }
}
+ journalString += "\"";
+
startOfReply = false;
}
@@ -502,11 +534,13 @@ void Journal::loadJournalFile(bool alreadyLoaded) {
justChangedSpeaker = true;
}
+ bool addPrefixThen = false;
+
if (!startOfReply) {
if (!commentFlag && !commentJustPrinted)
journalString += "\"\n";
- journalString += "Then ";
+ addPrefixThen = true; // "Then" should get added to the sentence
commentFlag = false;
} else if (!replyOnly) {
journalString += "\"\n";
@@ -517,15 +551,6 @@ void Journal::loadJournalFile(bool alreadyLoaded) {
if (IS_ROSE_TATTOO)
replyP++;
- if (c == 0)
- journalString += "Holmes";
- else if (c == 1)
- journalString += "I";
- else if (c == inspectorId)
- journalString += "the Inspector";
- else
- journalString += people._characters[c]._name;
-
if (IS_SERRATED_SCALPEL && _vm->getLanguage() == Common::DE_DEU)
Scalpel::ScalpelTalk::skipBadText(replyP);
@@ -535,10 +560,70 @@ void Journal::loadJournalFile(bool alreadyLoaded) {
v = *strP++;
} while (v && v < opcodes[0] && v != '.' && v != '!' && v != '?');
+ bool asked = false;
+
if (v == '?')
- journalString += " asked, \"";
- else
- journalString += " said, \"";
+ asked = true;
+
+ if (c == 0) {
+ // Holmes
+ if (addPrefixThen) {
+ if (asked)
+ journalString += fixedText.getJournalText(kFixedJournalText_ThenHolmesAsked);
+ else
+ journalString += fixedText.getJournalText(kFixedJournalText_ThenHolmesSaid);
+ } else {
+ if (asked)
+ journalString += fixedText.getJournalText(kFixedJournalText_HolmesAsked);
+ else
+ journalString += fixedText.getJournalText(kFixedJournalText_HolmesSaid);
+ }
+ } else if (c == 1) {
+ // I (Watson)
+ if (addPrefixThen) {
+ if (asked)
+ journalString += fixedText.getJournalText(kFixedJournalText_ThenIAsked);
+ else
+ journalString += fixedText.getJournalText(kFixedJournalText_ThenISaid);
+ } else {
+ if (asked)
+ journalString += fixedText.getJournalText(kFixedJournalText_IAsked);
+ else
+ journalString += fixedText.getJournalText(kFixedJournalText_ISaid);
+ }
+ } else if (c == inspectorId) {
+ // The Inspector
+ if (addPrefixThen) {
+ if (asked)
+ journalString += fixedText.getJournalText(kFixedJournalText_ThenTheInspectorAsked);
+ else
+ journalString += fixedText.getJournalText(kFixedJournalText_ThenTheInspectorSaid);
+ } else {
+ if (asked)
+ journalString += fixedText.getJournalText(kFixedJournalText_TheInspectorAsked);
+ else
+ journalString += fixedText.getJournalText(kFixedJournalText_TheInspectorSaid);
+ }
+ } else {
+ // Person
+ const char *text = nullptr;
+ if (addPrefixThen) {
+ if (asked)
+ text = fixedText.getJournalText(kFixedJournalText_ThenPersonAsked);
+ else
+ text = fixedText.getJournalText(kFixedJournalText_ThenPersonSaid);
+ } else {
+ if (asked)
+ text = fixedText.getJournalText(kFixedJournalText_PersonAsked);
+ else
+ text = fixedText.getJournalText(kFixedJournalText_PersonSaid);
+ }
+
+ journalString += Common::String::format(text, people._characters[c]._name);
+ }
+
+ journalString += "\"";
+
} else {
if (IS_SERRATED_SCALPEL) {
// Control code, so move past it and any parameters
@@ -718,8 +803,7 @@ bool Journal::isPrintable(byte ch) const {
if (ch < opcodes[0])
return true;
- if (_vm->getGameID() == GType_SerratedScalpel && _vm->getLanguage() == Common::DE_DEU
- && ch >= 0xe0)
+ if (_vm->getLanguage() == Common::DE_DEU && ch == 0xe1) // accept German Sharp-S character
return true;
return false;
diff --git a/engines/sherlock/module.mk b/engines/sherlock/module.mk
index 7fa7896691..0d17d0b249 100644
--- a/engines/sherlock/module.mk
+++ b/engines/sherlock/module.mk
@@ -3,6 +3,7 @@ MODULE := engines/sherlock
MODULE_OBJS = \
scalpel/scalpel.o \
scalpel/3do/movie_decoder.o \
+ scalpel/3do/scalpel_3do_screen.o \
scalpel/drivers/adlib.o \
scalpel/drivers/mt32.o \
scalpel/tsage/logo.o \
@@ -30,6 +31,7 @@ MODULE_OBJS = \
tattoo/tattoo_people.o \
tattoo/tattoo_resources.o \
tattoo/tattoo_scene.o \
+ tattoo/tattoo_screen.o \
tattoo/tattoo_talk.o \
tattoo/tattoo_user_interface.o \
tattoo/widget_base.o \
diff --git a/engines/sherlock/objects.cpp b/engines/sherlock/objects.cpp
index e70b707404..644c0c74c9 100644
--- a/engines/sherlock/objects.cpp
+++ b/engines/sherlock/objects.cpp
@@ -365,8 +365,8 @@ bool BaseObject::checkEndOfSequence() {
if (seq == 99) {
--_frameNumber;
- screen._backBuffer1.transBlitFrom(*_imageFrame, _position);
- screen._backBuffer2.transBlitFrom(*_imageFrame, _position);
+ screen._backBuffer1.SHtransBlitFrom(*_imageFrame, _position);
+ screen._backBuffer2.SHtransBlitFrom(*_imageFrame, _position);
_type = INVALID;
} else if (IS_ROSE_TATTOO && _talkSeq && seq == 0) {
setObjTalkSequence(_talkSeq);
@@ -1061,6 +1061,11 @@ void Object::load(Common::SeekableReadStream &s, bool isRoseTattoo) {
for (int idx = 0; idx < 6; ++idx)
_use[idx].load(s, true);
+ // WORKAROUND: Fix German version using hatpin/pin in pillow in Pratt's loft
+ if (_use[1]._target == "Nadel" && _use[1]._verb == "Untersuche"
+ && _use[2]._target == "Nadel" && _use[2]._verb == "Untersuche")
+ _use[1]._target = "Alte Nadel";
+
_quickDraw = s.readByte();
_scaleVal = s.readUint16LE();
_requiredFlag[1] = s.readSint16LE();
@@ -1338,7 +1343,7 @@ void Object::adjustObject() {
frame = 0;
int imgNum = _sequences[frame];
- if (imgNum > _maxFrames)
+ if (imgNum > _maxFrames || imgNum == 0)
imgNum = 1;
_imageFrame = &(*_images)[imgNum - 1];
@@ -1424,8 +1429,19 @@ int Object::pickUpObject(FixedTextActionId fixedTextActionId) {
ui.clearInfo();
Common::String itemName = _description;
- itemName.setChar(tolower(itemName[0]), 0);
- screen.print(Common::Point(0, INFO_LINE + 1), COL_INFO_FOREGROUND, "Picked up %s", itemName.c_str());
+
+ // It's an item, make it lowercase
+ switch (_vm->getLanguage()) {
+ case Common::DE_DEU:
+ // don't do this for German version
+ break;
+ default:
+ // do it for English + Spanish version
+ itemName.setChar(tolower(itemName[0]), 0);
+ break;
+ }
+
+ screen.print(Common::Point(0, INFO_LINE + 1), COL_INFO_FOREGROUND, fixedText.getObjectPickedUpText(), itemName.c_str());
ui._menuCounter = 25;
}
}
diff --git a/engines/sherlock/scalpel/3do/scalpel_3do_screen.cpp b/engines/sherlock/scalpel/3do/scalpel_3do_screen.cpp
new file mode 100644
index 0000000000..f848394864
--- /dev/null
+++ b/engines/sherlock/scalpel/3do/scalpel_3do_screen.cpp
@@ -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.
+ *
+ */
+
+#include "sherlock/scalpel/scalpel_screen.h"
+#include "sherlock/scalpel/scalpel.h"
+#include "sherlock/scalpel/3do/scalpel_3do_screen.h"
+
+namespace Sherlock {
+
+namespace Scalpel {
+
+Scalpel3DOScreen::Scalpel3DOScreen(SherlockEngine *vm): ScalpelScreen(vm) {
+}
+
+void Scalpel3DOScreen::SHblitFrom(const Graphics::Surface &src, const Common::Point &pt, const Common::Rect &srcBounds) {
+ if (!_vm->_isScreenDoubled) {
+ ScalpelScreen::SHblitFrom(src, pt, srcBounds);
+ return;
+ }
+
+ Common::Rect srcRect = srcBounds;
+ Common::Rect destRect(pt.x, pt.y, pt.x + srcRect.width(), pt.y + srcRect.height());
+
+ if (!srcRect.isValidRect() || !clip(srcRect, destRect))
+ return;
+
+ // Add dirty area remapped to the 640x200 surface
+ addDirtyRect(Common::Rect(destRect.left * 2, destRect.top * 2, destRect.right * 2, destRect.bottom * 2));
+
+ // Transfer the area, doubling each pixel
+ for (int yp = 0; yp < srcRect.height(); ++yp) {
+ const uint16 *srcP = (const uint16 *)src.getBasePtr(srcRect.left, srcRect.top + yp);
+ uint16 *destP = (uint16 *)getBasePtr(destRect.left * 2, (destRect.top + yp) * 2);
+
+ for (int xp = srcRect.left; xp < srcRect.right; ++xp, ++srcP, destP += 2) {
+ *destP = *srcP;
+ *(destP + 1) = *srcP;
+ *(destP + 640) = *srcP;
+ *(destP + 640 + 1) = *srcP;
+ }
+ }
+}
+
+void Scalpel3DOScreen::transBlitFromUnscaled(const Graphics::Surface &src, const Common::Point &pt,
+ bool flipped, int overrideColor) {
+ error("TODO: Refactor");
+#if 0
+ if (!_vm->_isScreenDoubled) {
+ ScalpelScreen::transBlitFromUnscaled(src, pt, flipped, overrideColor);
+ return;
+ }
+
+ Common::Rect drawRect(0, 0, src.w, src.h);
+ Common::Rect destRect(pt.x, pt.y, pt.x + src.w, pt.y + src.h);
+
+ // Clip the display area to on-screen
+ if (!clip(drawRect, destRect))
+ // It's completely off-screen
+ return;
+
+ if (flipped)
+ drawRect = Common::Rect(src.w - drawRect.right, src.h - drawRect.bottom,
+ src.w - drawRect.left, src.h - drawRect.top);
+
+ Common::Point destPt(destRect.left, destRect.top);
+ addDirtyRect(Common::Rect(destPt.x * 2, destPt.y * 2, (destPt.x + drawRect.width()) * 2,
+ (destPt.y + drawRect.height()) * 2));
+
+ assert(src.format.bytesPerPixel == 2 && _surface.format.bytesPerPixel == 2);
+
+ for (int yp = 0; yp < drawRect.height(); ++yp) {
+ const uint16 *srcP = (const uint16 *)src.getBasePtr(
+ flipped ? drawRect.right - 1 : drawRect.left, drawRect.top + yp);
+ uint16 *destP = (uint16 *)getBasePtr(destPt.x * 2, (destPt.y + yp) * 2);
+
+ for (int xp = 0; xp < drawRect.width(); ++xp, destP += 2) {
+ // RGB 0, 0, 0 -> transparent on 3DO
+ if (*srcP) {
+ *destP = *srcP;
+ *(destP + 1) = *srcP;
+ *(destP + 640) = *srcP;
+ *(destP + 640 + 1) = *srcP;
+ }
+
+ srcP = flipped ? srcP - 1 : srcP + 1;
+ }
+ }
+#endif
+}
+
+void Scalpel3DOScreen::fillRect(const Common::Rect &r, uint color) {
+ if (_vm->_isScreenDoubled)
+ ScalpelScreen::fillRect(Common::Rect(r.left * 2, r.top * 2, r.right * 2, r.bottom * 2), color);
+ else
+ ScalpelScreen::fillRect(r, color);
+}
+
+void Scalpel3DOScreen::fadeIntoScreen3DO(int speed) {
+ Events &events = *_vm->_events;
+ uint16 *currentScreenBasePtr = (uint16 *)getPixels();
+ uint16 *targetScreenBasePtr = (uint16 *)_backBuffer->getPixels();
+ uint16 currentScreenPixel = 0;
+ uint16 targetScreenPixel = 0;
+
+ uint16 currentScreenPixelRed = 0;
+ uint16 currentScreenPixelGreen = 0;
+ uint16 currentScreenPixelBlue = 0;
+
+ uint16 targetScreenPixelRed = 0;
+ uint16 targetScreenPixelGreen = 0;
+ uint16 targetScreenPixelBlue = 0;
+
+ uint16 screenWidth = SHERLOCK_SCREEN_WIDTH;
+ uint16 screenHeight = SHERLOCK_SCREEN_HEIGHT;
+ uint16 screenX = 0;
+ uint16 screenY = 0;
+ uint16 pixelsChanged = 0;
+
+ clearDirtyRects();
+
+ do {
+ pixelsChanged = 0;
+ uint16 *currentScreenPtr = currentScreenBasePtr;
+ uint16 *targetScreenPtr = targetScreenBasePtr;
+
+ for (screenY = 0; screenY < screenHeight; screenY++) {
+ for (screenX = 0; screenX < screenWidth; screenX++) {
+ currentScreenPixel = *currentScreenPtr;
+ targetScreenPixel = *targetScreenPtr;
+
+ if (currentScreenPixel != targetScreenPixel) {
+ // pixel doesn't match, adjust accordingly
+ currentScreenPixelRed = currentScreenPixel & 0xF800;
+ currentScreenPixelGreen = currentScreenPixel & 0x07E0;
+ currentScreenPixelBlue = currentScreenPixel & 0x001F;
+ targetScreenPixelRed = targetScreenPixel & 0xF800;
+ targetScreenPixelGreen = targetScreenPixel & 0x07E0;
+ targetScreenPixelBlue = targetScreenPixel & 0x001F;
+
+ if (currentScreenPixelRed != targetScreenPixelRed) {
+ if (currentScreenPixelRed < targetScreenPixelRed) {
+ currentScreenPixelRed += 0x0800;
+ } else {
+ currentScreenPixelRed -= 0x0800;
+ }
+ }
+ if (currentScreenPixelGreen != targetScreenPixelGreen) {
+ // Adjust +2/-2 because we are running RGB555 at RGB565
+ if (currentScreenPixelGreen < targetScreenPixelGreen) {
+ currentScreenPixelGreen += 0x0040;
+ } else {
+ currentScreenPixelGreen -= 0x0040;
+ }
+ }
+ if (currentScreenPixelBlue != targetScreenPixelBlue) {
+ if (currentScreenPixelBlue < targetScreenPixelBlue) {
+ currentScreenPixelBlue += 0x0001;
+ } else {
+ currentScreenPixelBlue -= 0x0001;
+ }
+ }
+
+ uint16 v = currentScreenPixelRed | currentScreenPixelGreen | currentScreenPixelBlue;
+ *currentScreenPtr = v;
+ if (_vm->_isScreenDoubled) {
+ *(currentScreenPtr + 1) = v;
+ *(currentScreenPtr + 640) = v;
+ *(currentScreenPtr + 640 + 1) = v;
+ }
+
+ pixelsChanged++;
+ }
+
+ currentScreenPtr += _vm->_isScreenDoubled ? 2 : 1;
+ targetScreenPtr++;
+ }
+
+ if (_vm->_isScreenDoubled)
+ currentScreenPtr += 640;
+ }
+
+ // Too much considered dirty at the moment
+ if (_vm->_isScreenDoubled)
+ addDirtyRect(Common::Rect(0, 0, screenWidth * 2, screenHeight * 2));
+ else
+ addDirtyRect(Common::Rect(0, 0, screenWidth, screenHeight));
+
+ events.pollEvents();
+ events.delay(10 * speed);
+ } while ((pixelsChanged) && (!_vm->shouldQuit()));
+}
+
+void Scalpel3DOScreen::blitFrom3DOcolorLimit(uint16 limitColor) {
+ uint16 *currentScreenPtr = (uint16 *)getPixels();
+ uint16 *targetScreenPtr = (uint16 *)_backBuffer->getPixels();
+ uint16 currentScreenPixel = 0;
+
+ uint16 screenWidth = SHERLOCK_SCREEN_WIDTH;
+ uint16 screenHeight = SHERLOCK_SCREEN_HEIGHT;
+ uint16 screenX = 0;
+ uint16 screenY = 0;
+
+ uint16 currentScreenPixelRed = 0;
+ uint16 currentScreenPixelGreen = 0;
+ uint16 currentScreenPixelBlue = 0;
+
+ uint16 limitPixelRed = limitColor & 0xF800;
+ uint16 limitPixelGreen = limitColor & 0x07E0;
+ uint16 limitPixelBlue = limitColor & 0x001F;
+
+ for (screenY = 0; screenY < screenHeight; screenY++) {
+ for (screenX = 0; screenX < screenWidth; screenX++) {
+ currentScreenPixel = *targetScreenPtr;
+
+ currentScreenPixelRed = currentScreenPixel & 0xF800;
+ currentScreenPixelGreen = currentScreenPixel & 0x07E0;
+ currentScreenPixelBlue = currentScreenPixel & 0x001F;
+
+ if (currentScreenPixelRed < limitPixelRed)
+ currentScreenPixelRed = limitPixelRed;
+ if (currentScreenPixelGreen < limitPixelGreen)
+ currentScreenPixelGreen = limitPixelGreen;
+ if (currentScreenPixelBlue < limitPixelBlue)
+ currentScreenPixelBlue = limitPixelBlue;
+
+ uint16 v = currentScreenPixelRed | currentScreenPixelGreen | currentScreenPixelBlue;
+ *currentScreenPtr = v;
+ if (_vm->_isScreenDoubled) {
+ *(currentScreenPtr + 1) = v;
+ *(currentScreenPtr + 640) = v;
+ *(currentScreenPtr + 640 + 1) = v;
+ }
+
+ currentScreenPtr += _vm->_isScreenDoubled ? 2 : 1;
+ targetScreenPtr++;
+ }
+
+ if (_vm->_isScreenDoubled)
+ currentScreenPtr += 640;
+ }
+
+ // Too much considered dirty at the moment
+ if (_vm->_isScreenDoubled)
+ addDirtyRect(Common::Rect(0, 0, screenWidth * 2, screenHeight * 2));
+ else
+ addDirtyRect(Common::Rect(0, 0, screenWidth, screenHeight));
+}
+
+uint16 Scalpel3DOScreen::width() const {
+ return _vm->_isScreenDoubled ? this->w / 2 : this->w;
+}
+
+uint16 Scalpel3DOScreen::height() const {
+ return _vm->_isScreenDoubled ? this->h / 2 : this->h;
+}
+
+void Scalpel3DOScreen::rawBlitFrom(const Graphics::Surface &src, const Common::Point &pt) {
+ Common::Rect srcRect(0, 0, src.w, src.h);
+ Common::Rect destRect(pt.x, pt.y, pt.x + src.w, pt.y + src.h);
+
+ addDirtyRect(destRect);
+ copyRectToSurface(src, destRect.left, destRect.top, srcRect);
+}
+
+} // End of namespace Scalpel
+
+} // End of namespace Sherlock
diff --git a/engines/sherlock/scalpel/3do/scalpel_3do_screen.h b/engines/sherlock/scalpel/3do/scalpel_3do_screen.h
new file mode 100644
index 0000000000..422f588b17
--- /dev/null
+++ b/engines/sherlock/scalpel/3do/scalpel_3do_screen.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.
+ *
+ */
+
+#ifndef SHERLOCK_SCALPEL_3DO_SCREEN_H
+#define SHERLOCK_SCALPEL_3DO_SCREEN_H
+
+#include "sherlock/scalpel/scalpel_screen.h"
+
+namespace Sherlock {
+
+class SherlockEngine;
+
+namespace Scalpel {
+
+class Scalpel3DOScreen : public ScalpelScreen {
+protected:
+ /**
+ * Draws a sub-section of a surface at a given position within this surface
+ * Overriden for the 3DO to automatically double the size of everything to the underlying 640x400 surface
+ */
+ virtual void SHblitFrom(const Graphics::Surface &src, const Common::Point &pt, const Common::Rect &srcBounds);
+
+ /**
+ * Draws a surface at a given position within this surface with transparency
+ */
+ virtual void transBlitFromUnscaled(const Graphics::Surface &src, const Common::Point &pt, bool flipped,
+ int overrideColor);
+public:
+ Scalpel3DOScreen(SherlockEngine *vm);
+ virtual ~Scalpel3DOScreen() {}
+
+ /**
+ * Draws a sub-section of a surface at a given position within this surface
+ */
+ void rawBlitFrom(const Graphics::Surface &src, const Common::Point &pt);
+
+ /**
+ * Fade backbuffer 1 into screen (3DO RGB!)
+ */
+ void fadeIntoScreen3DO(int speed);
+
+ void blitFrom3DOcolorLimit(uint16 color);
+
+ /**
+ * Fill a given area of the surface with a given color
+ */
+ virtual void fillRect(const Common::Rect &r, uint color);
+
+ virtual uint16 width() const;
+ virtual uint16 height() const;
+};
+
+} // End of namespace Scalpel
+
+} // End of namespace Sherlock
+
+#endif
diff --git a/engines/sherlock/scalpel/scalpel.cpp b/engines/sherlock/scalpel/scalpel.cpp
index b17f38b218..cbb202095f 100644
--- a/engines/sherlock/scalpel/scalpel.cpp
+++ b/engines/sherlock/scalpel/scalpel.cpp
@@ -29,6 +29,7 @@
#include "sherlock/scalpel/scalpel_people.h"
#include "sherlock/scalpel/scalpel_scene.h"
#include "sherlock/scalpel/scalpel_screen.h"
+#include "sherlock/scalpel/3do/scalpel_3do_screen.h"
#include "sherlock/scalpel/tsage/logo.h"
#include "sherlock/sherlock.h"
#include "sherlock/music.h"
@@ -371,8 +372,8 @@ bool ScalpelEngine::showCityCutscene() {
if (finished) {
ImageFile titleImages_LondonNovember("title2.vgs", true);
- _screen->_backBuffer1.blitFrom(*_screen);
- _screen->_backBuffer2.blitFrom(*_screen);
+ _screen->_backBuffer1.SHblitFrom(*_screen);
+ _screen->_backBuffer2.SHblitFrom(*_screen);
Common::Point londonPosition;
@@ -386,19 +387,19 @@ bool ScalpelEngine::showCityCutscene() {
}
// London, England
- _screen->_backBuffer1.transBlitFrom(titleImages_LondonNovember[0], londonPosition);
+ _screen->_backBuffer1.SHtransBlitFrom(titleImages_LondonNovember[0], londonPosition);
_screen->randomTransition();
finished = _events->delay(1000, true);
// November, 1888
if (finished) {
- _screen->_backBuffer1.transBlitFrom(titleImages_LondonNovember[1], Common::Point(100, 100));
+ _screen->_backBuffer1.SHtransBlitFrom(titleImages_LondonNovember[1], Common::Point(100, 100));
_screen->randomTransition();
finished = _events->delay(5000, true);
}
// Transition out the title
- _screen->_backBuffer1.blitFrom(_screen->_backBuffer2);
+ _screen->_backBuffer1.SHblitFrom(_screen->_backBuffer2);
_screen->randomTransition();
}
@@ -407,8 +408,8 @@ bool ScalpelEngine::showCityCutscene() {
if (finished) {
ImageFile titleImages_SherlockHolmesTitle("title.vgs", true);
- _screen->_backBuffer1.blitFrom(*_screen);
- _screen->_backBuffer2.blitFrom(*_screen);
+ _screen->_backBuffer1.SHblitFrom(*_screen);
+ _screen->_backBuffer2.SHblitFrom(*_screen);
Common::Point lostFilesPosition;
Common::Point sherlockHolmesPosition;
@@ -427,17 +428,17 @@ bool ScalpelEngine::showCityCutscene() {
}
// The Lost Files of
- _screen->_backBuffer1.transBlitFrom(titleImages_SherlockHolmesTitle[0], lostFilesPosition);
+ _screen->_backBuffer1.SHtransBlitFrom(titleImages_SherlockHolmesTitle[0], lostFilesPosition);
// Sherlock Holmes
- _screen->_backBuffer1.transBlitFrom(titleImages_SherlockHolmesTitle[1], sherlockHolmesPosition);
+ _screen->_backBuffer1.SHtransBlitFrom(titleImages_SherlockHolmesTitle[1], sherlockHolmesPosition);
// copyright
- _screen->_backBuffer1.transBlitFrom(titleImages_SherlockHolmesTitle[2], copyrightPosition);
+ _screen->_backBuffer1.SHtransBlitFrom(titleImages_SherlockHolmesTitle[2], copyrightPosition);
_screen->verticalTransition();
finished = _events->delay(4000, true);
if (finished) {
- _screen->_backBuffer1.blitFrom(_screen->_backBuffer2);
+ _screen->_backBuffer1.SHblitFrom(_screen->_backBuffer2);
_screen->randomTransition();
finished = _events->delay(2000);
}
@@ -461,7 +462,7 @@ bool ScalpelEngine::showCityCutscene() {
// English, width 175, height 38
alleyPosition = Common::Point(72, 51);
}
- _screen->transBlitFrom(titleImages_SherlockHolmesTitle[3], alleyPosition);
+ _screen->SHtransBlitFrom(titleImages_SherlockHolmesTitle[3], alleyPosition);
_screen->fadeIn(palette, 3);
// Wait until the track got looped and the first few notes were played
@@ -537,7 +538,7 @@ bool ScalpelEngine::showAlleyCutscene() {
earlyTheFollowingMorningPosition = Common::Point(35, 52);
}
- _screen->transBlitFrom(titleImages_EarlyTheFollowingMorning[0], earlyTheFollowingMorningPosition);
+ _screen->SHtransBlitFrom(titleImages_EarlyTheFollowingMorning[0], earlyTheFollowingMorningPosition);
// fast fade-in
_screen->fadeIn(palette, 1);
@@ -641,23 +642,23 @@ bool ScalpelEngine::scrollCredits() {
delete stream;
// Save a copy of the screen background for use in drawing each credit frame
- _screen->_backBuffer1.blitFrom(*_screen);
+ _screen->_backBuffer1.SHblitFrom(*_screen);
// Loop for showing the credits
for(int idx = 0; idx < 600 && !_events->kbHit() && !shouldQuit(); ++idx) {
// Copy the entire screen background before writing text
- _screen->blitFrom(_screen->_backBuffer1);
+ _screen->SHblitFrom(_screen->_backBuffer1);
// Write the text appropriate for the next frame
if (idx < 400)
- _screen->transBlitFrom(creditsImages[0], Common::Point(10, 200 - idx), false, 0);
+ _screen->SHtransBlitFrom(creditsImages[0], Common::Point(10, 200 - idx), false, 0);
if (idx > 200)
- _screen->transBlitFrom(creditsImages[1], Common::Point(10, 400 - idx), false, 0);
+ _screen->SHtransBlitFrom(creditsImages[1], Common::Point(10, 400 - idx), false, 0);
// Don't show credit text on the top and bottom ten rows of the screen
- _screen->blitFrom(_screen->_backBuffer1, Common::Point(0, 0), Common::Rect(0, 0, _screen->w(), 10));
- _screen->blitFrom(_screen->_backBuffer1, Common::Point(0, _screen->h() - 10),
- Common::Rect(0, _screen->h() - 10, _screen->w(), _screen->h()));
+ _screen->SHblitFrom(_screen->_backBuffer1, Common::Point(0, 0), Common::Rect(0, 0, _screen->width(), 10));
+ _screen->SHblitFrom(_screen->_backBuffer1, Common::Point(0, _screen->height() - 10),
+ Common::Rect(0, _screen->height() - 10, _screen->width(), _screen->height()));
_events->delay(100);
}
@@ -670,7 +671,7 @@ bool ScalpelEngine::show3DOSplash() {
// 3DO EA Splash screen
ImageFile3DO titleImage_3DOSplash("3DOSplash.cel", kImageFile3DOType_Cel);
- _screen->transBlitFrom(titleImage_3DOSplash[0]._frame, Common::Point(0, -20));
+ _screen->SHtransBlitFrom(titleImage_3DOSplash[0]._frame, Common::Point(0, -20));
bool finished = _events->delay(3000, true);
if (finished) {
@@ -706,7 +707,7 @@ bool ScalpelEngine::showCityCutscene3DO() {
_sound->playAiff("prologue/sounds/rain.aiff", 15, true);
// Fade screen to grey
- screen._backBuffer1.fill(0xCE59); // RGB565: 25, 50, 25 (grey)
+ screen._backBuffer1.clear(0xCE59); // RGB565: 25, 50, 25 (grey)
screen.fadeIntoScreen3DO(2);
}
@@ -715,16 +716,16 @@ bool ScalpelEngine::showCityCutscene3DO() {
}
if (finished) {
- screen._backBuffer1.fill(0); // fill backbuffer with black to avoid issues during fade from white
+ screen._backBuffer1.clear(0); // fill backbuffer with black to avoid issues during fade from white
finished = _animation->play3DO("26open1", true, 1, true, 2);
}
if (finished) {
- screen._backBuffer2.blitFrom(screen._backBuffer1);
+ screen._backBuffer2.SHblitFrom(screen._backBuffer1);
// "London, England"
ImageFile3DO titleImage_London("title2a.cel", kImageFile3DOType_Cel);
- screen._backBuffer1.transBlitFrom(titleImage_London[0]._frame, Common::Point(30, 50));
+ screen._backBuffer1.SHtransBlitFrom(titleImage_London[0]._frame, Common::Point(30, 50));
screen.fadeIntoScreen3DO(1);
finished = _events->delay(1500, true);
@@ -732,7 +733,7 @@ bool ScalpelEngine::showCityCutscene3DO() {
if (finished) {
// "November, 1888"
ImageFile3DO titleImage_November("title2b.cel", kImageFile3DOType_Cel);
- screen._backBuffer1.transBlitFrom(titleImage_November[0]._frame, Common::Point(100, 100));
+ screen._backBuffer1.SHtransBlitFrom(titleImage_November[0]._frame, Common::Point(100, 100));
screen.fadeIntoScreen3DO(1);
finished = _music->waitUntilMSec(14700, 0, 0, 5000);
@@ -740,8 +741,8 @@ bool ScalpelEngine::showCityCutscene3DO() {
if (finished) {
// Restore screen
- _screen->_backBuffer1.blitFrom(screen._backBuffer2);
- _screen->blitFrom(screen._backBuffer1);
+ _screen->_backBuffer1.SHblitFrom(screen._backBuffer2);
+ _screen->SHblitFrom(screen._backBuffer1);
}
}
@@ -751,7 +752,7 @@ bool ScalpelEngine::showCityCutscene3DO() {
if (finished) {
// "Sherlock Holmes" (title)
ImageFile3DO titleImage_SherlockHolmesTitle("title1ab.cel", kImageFile3DOType_Cel);
- screen._backBuffer1.transBlitFrom(titleImage_SherlockHolmesTitle[0]._frame, Common::Point(34, 5));
+ screen._backBuffer1.SHtransBlitFrom(titleImage_SherlockHolmesTitle[0]._frame, Common::Point(34, 5));
// Blend in
screen.fadeIntoScreen3DO(2);
@@ -761,7 +762,7 @@ bool ScalpelEngine::showCityCutscene3DO() {
if (finished) {
ImageFile3DO titleImage_Copyright("title1c.cel", kImageFile3DOType_Cel);
- screen.transBlitFrom(titleImage_Copyright[0]._frame, Common::Point(20, 190));
+ screen.SHtransBlitFrom(titleImage_Copyright[0]._frame, Common::Point(20, 190));
finished = _events->delay(3500, true);
}
}
@@ -780,7 +781,7 @@ bool ScalpelEngine::showCityCutscene3DO() {
if (finished) {
// "In the alley behind the Regency Theatre..."
ImageFile3DO titleImage_InTheAlley("title1d.cel", kImageFile3DOType_Cel);
- screen._backBuffer1.transBlitFrom(titleImage_InTheAlley[0]._frame, Common::Point(72, 51));
+ screen._backBuffer1.SHtransBlitFrom(titleImage_InTheAlley[0]._frame, Common::Point(72, 51));
// Fade in
screen.fadeIntoScreen3DO(4);
@@ -819,7 +820,7 @@ bool ScalpelEngine::showAlleyCutscene3DO() {
ImageFile3DO titleImage_ScreamingVictim("scream.cel", kImageFile3DOType_Cel);
screen.clear();
- screen.transBlitFrom(titleImage_ScreamingVictim[0]._frame, Common::Point(0, 0));
+ screen.SHtransBlitFrom(titleImage_ScreamingVictim[0]._frame, Common::Point(0, 0));
// Play "scream.aiff"
if (_sound->_voices)
@@ -848,7 +849,7 @@ bool ScalpelEngine::showAlleyCutscene3DO() {
if (finished) {
// "Early the following morning on Baker Street..."
ImageFile3DO titleImage_EarlyTheFollowingMorning("title3.cel", kImageFile3DOType_Cel);
- screen._backBuffer1.transBlitFrom(titleImage_EarlyTheFollowingMorning[0]._frame, Common::Point(35, 51));
+ screen._backBuffer1.SHtransBlitFrom(titleImage_EarlyTheFollowingMorning[0]._frame, Common::Point(35, 51));
// Fade in
screen.fadeIntoScreen3DO(4);
@@ -908,7 +909,7 @@ bool ScalpelEngine::showOfficeCutscene3DO() {
ImageFile3DO titleImage_CoffeeNote("note.cel", kImageFile3DOType_Cel);
_screen->clear();
- _screen->transBlitFrom(titleImage_CoffeeNote[0]._frame, Common::Point(0, 0));
+ _screen->SHtransBlitFrom(titleImage_CoffeeNote[0]._frame, Common::Point(0, 0));
if (_sound->_voices) {
finished = _sound->playSound("prologue/sounds/note.aiff", WAIT_KBD_OR_FINISH);
@@ -937,7 +938,7 @@ bool ScalpelEngine::showOfficeCutscene3DO() {
// TODO: Brighten the image, possibly by doing a partial fade
// to white.
- _screen->_backBuffer2.blitFrom(_screen->_backBuffer1);
+ _screen->_backBuffer2.SHblitFrom(_screen->_backBuffer1);
for (int nr = 1; finished && nr <= 4; nr++) {
char filename[15];
@@ -945,8 +946,8 @@ bool ScalpelEngine::showOfficeCutscene3DO() {
ImageFile3DO *creditsImage = new ImageFile3DO(filename, kImageFile3DOType_Cel);
ImageFrame *creditsFrame = &(*creditsImage)[0];
for (int i = 0; finished && i < 200 + creditsFrame->_height; i++) {
- _screen->blitFrom(_screen->_backBuffer2);
- _screen->transBlitFrom(creditsFrame->_frame, Common::Point((320 - creditsFrame->_width) / 2, 200 - i));
+ _screen->SHblitFrom(_screen->_backBuffer2);
+ _screen->SHtransBlitFrom(creditsFrame->_frame, Common::Point((320 - creditsFrame->_width) / 2, 200 - i));
if (!_events->delay(70, true))
finished = false;
}
@@ -998,7 +999,7 @@ void ScalpelEngine::showLBV(const Common::String &filename) {
delete stream;
_screen->setPalette(images._palette);
- _screen->_backBuffer1.blitFrom(images[0]);
+ _screen->_backBuffer1.SHblitFrom(images[0]);
_screen->verticalTransition();
}
@@ -1156,7 +1157,7 @@ void ScalpelEngine::eraseBrumwellMirror() {
// If player is in range of the mirror, then restore background from the secondary back buffer
if (Common::Rect(70, 100, 200, 200).contains(pt)) {
- _screen->_backBuffer1.blitFrom(_screen->_backBuffer2, Common::Point(137, 18),
+ _screen->_backBuffer1.SHblitFrom(_screen->_backBuffer2, Common::Point(137, 18),
Common::Rect(137, 18, 184, 74));
}
}
@@ -1218,20 +1219,20 @@ void ScalpelEngine::doBrumwellMirror() {
bool flipped = people[HOLMES]._sequenceNumber == WALK_LEFT || people[HOLMES]._sequenceNumber == STOP_LEFT
|| people[HOLMES]._sequenceNumber == WALK_UPRIGHT || people[HOLMES]._sequenceNumber == STOP_UPRIGHT
|| people[HOLMES]._sequenceNumber == WALK_DOWNLEFT || people[HOLMES]._sequenceNumber == STOP_DOWNLEFT;
- _screen->_backBuffer1.transBlitFrom(imageFrame, pt + Common::Point(38, -imageFrame._frame.h - 25), flipped);
+ _screen->_backBuffer1.SHtransBlitFrom(imageFrame, pt + Common::Point(38, -imageFrame._frame.h - 25), flipped);
// Redraw the mirror borders to prevent the drawn image of Holmes from appearing outside of the mirror
- _screen->_backBuffer1.blitFrom(_screen->_backBuffer2, Common::Point(114, 18),
+ _screen->_backBuffer1.SHblitFrom(_screen->_backBuffer2, Common::Point(114, 18),
Common::Rect(114, 18, 137, 114));
- _screen->_backBuffer1.blitFrom(_screen->_backBuffer2, Common::Point(137, 70),
+ _screen->_backBuffer1.SHblitFrom(_screen->_backBuffer2, Common::Point(137, 70),
Common::Rect(137, 70, 142, 114));
- _screen->_backBuffer1.blitFrom(_screen->_backBuffer2, Common::Point(142, 71),
+ _screen->_backBuffer1.SHblitFrom(_screen->_backBuffer2, Common::Point(142, 71),
Common::Rect(142, 71, 159, 114));
- _screen->_backBuffer1.blitFrom(_screen->_backBuffer2, Common::Point(159, 72),
+ _screen->_backBuffer1.SHblitFrom(_screen->_backBuffer2, Common::Point(159, 72),
Common::Rect(159, 72, 170, 116));
- _screen->_backBuffer1.blitFrom(_screen->_backBuffer2, Common::Point(170, 73),
+ _screen->_backBuffer1.SHblitFrom(_screen->_backBuffer2, Common::Point(170, 73),
Common::Rect(170, 73, 184, 114));
- _screen->_backBuffer1.blitFrom(_screen->_backBuffer2, Common::Point(184, 18),
+ _screen->_backBuffer1.SHblitFrom(_screen->_backBuffer2, Common::Point(184, 18),
Common::Rect(184, 18, 212, 114));
}
}
@@ -1272,7 +1273,7 @@ void ScalpelEngine::showScummVMRestoreDialog() {
bool ScalpelEngine::play3doMovie(const Common::String &filename, const Common::Point &pos, bool isPortrait) {
Scalpel3DOScreen &screen = *(Scalpel3DOScreen *)_screen;
Scalpel3DOMovieDecoder *videoDecoder = new Scalpel3DOMovieDecoder();
- Graphics::Surface tempSurface;
+ Graphics::ManagedSurface tempSurface;
Common::Point framePos(pos.x, pos.y);
ImageFile3DO *frameImageFile = nullptr;
@@ -1307,7 +1308,7 @@ bool ScalpelEngine::play3doMovie(const Common::String &filename, const Common::P
// If we're to show the movie at half-size, we'll need a temporary intermediate surface
if (halfSize)
- tempSurface.create(width / 2, height / 2, _screen->getPixelFormat());
+ tempSurface.create(width / 2, height / 2);
while (!shouldQuit() && !videoDecoder->endOfVideo() && !skipVideo) {
if (videoDecoder->needsUpdate()) {
@@ -1371,19 +1372,19 @@ bool ScalpelEngine::play3doMovie(const Common::String &filename, const Common::P
}
// Point the drawing frame to the temporary surface
- frame = &tempSurface;
+ frame = &tempSurface.rawSurface();
}
if (isPortrait && !frameShown) {
// Draw the frame (not the frame of the video, but a frame around the video) itself
- _screen->transBlitFrom(frameImage->_frame, framePos);
+ _screen->SHtransBlitFrom(frameImage->_frame, framePos);
frameShown = true;
}
if (isPortrait && !halfSize) {
screen.rawBlitFrom(*frame, Common::Point(pos.x * 2, pos.y * 2));
} else {
- _screen->blitFrom(*frame, pos);
+ _screen->SHblitFrom(*frame, pos);
}
_screen->update();
@@ -1413,9 +1414,9 @@ bool ScalpelEngine::play3doMovie(const Common::String &filename, const Common::P
}
// Restore scene
- screen._backBuffer1.blitFrom(screen._backBuffer2);
+ screen._backBuffer1.SHblitFrom(screen._backBuffer2);
_scene->updateBackground();
- screen.slamArea(0, 0, screen.w(), CONTROLS_Y);
+ screen.slamArea(0, 0, screen.width(), CONTROLS_Y);
return !skipVideo;
}
diff --git a/engines/sherlock/scalpel/scalpel_darts.cpp b/engines/sherlock/scalpel/scalpel_darts.cpp
index 87f4566837..c5ba8032f3 100644
--- a/engines/sherlock/scalpel/scalpel_darts.cpp
+++ b/engines/sherlock/scalpel/scalpel_darts.cpp
@@ -102,7 +102,7 @@ void Darts::playDarts() {
score -= lastDart;
_roundScore += lastDart;
- screen._backBuffer1.blitFrom(screen._backBuffer2, Common::Point(DART_INFO_X, DART_INFO_Y - 1),
+ screen._backBuffer1.SHblitFrom(screen._backBuffer2, Common::Point(DART_INFO_X, DART_INFO_Y - 1),
Common::Rect(DART_INFO_X, DART_INFO_Y - 1, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT));
screen.print(Common::Point(DART_INFO_X, DART_INFO_Y), DART_COL_FORE, "Dart # %d", idx + 1);
screen.print(Common::Point(DART_INFO_X, DART_INFO_Y + 10), DART_COL_FORE, "Scored %d points", lastDart);
@@ -154,7 +154,7 @@ void Darts::playDarts() {
events.wait(20);
}
- screen._backBuffer1.blitFrom(screen._backBuffer2, Common::Point(DART_INFO_X, DART_INFO_Y - 1),
+ screen._backBuffer1.SHblitFrom(screen._backBuffer2, Common::Point(DART_INFO_X, DART_INFO_Y - 1),
Common::Rect(DART_INFO_X, DART_INFO_Y - 1, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT));
screen.slamArea(0, 0, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT);
}
@@ -166,8 +166,8 @@ void Darts::playDarts() {
done |= _vm->shouldQuit();
if (!done) {
- screen._backBuffer2.blitFrom((*_dartImages)[0], Common::Point(0, 0));
- screen._backBuffer1.blitFrom(screen._backBuffer2);
+ screen._backBuffer2.SHblitFrom((*_dartImages)[0], Common::Point(0, 0));
+ screen._backBuffer1.SHblitFrom(screen._backBuffer2);
screen.slamArea(0, 0, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT);
}
} while (!done);
@@ -185,7 +185,7 @@ void Darts::loadDarts() {
_dartImages = new ImageFile("darts.vgs");
screen.setPalette(_dartImages->_palette);
- screen._backBuffer1.blitFrom((*_dartImages)[0], Common::Point(0, 0));
+ screen._backBuffer1.SHblitFrom((*_dartImages)[0], Common::Point(0, 0));
screen.slamArea(0, 0, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT);
}
@@ -245,7 +245,7 @@ void Darts::showNames(int playerNum) {
screen.slamArea(STATUS_INFO_X + 50, STATUS_INFO_Y + 10, 81, 12);
// Make a copy of the back buffer to the secondary one
- screen._backBuffer2.blitFrom(screen._backBuffer1);
+ screen._backBuffer2.SHblitFrom(screen._backBuffer1);
}
void Darts::showStatus(int playerNum) {
@@ -253,7 +253,7 @@ void Darts::showStatus(int playerNum) {
byte color;
// Copy scoring screen from secondary back buffer. This will erase any previously displayed status/score info
- screen._backBuffer1.blitFrom(screen._backBuffer2, Common::Point(STATUS_INFO_X, STATUS_INFO_Y + 10),
+ screen._backBuffer1.SHblitFrom(screen._backBuffer2, Common::Point(STATUS_INFO_X, STATUS_INFO_Y + 10),
Common::Rect(STATUS_INFO_X, STATUS_INFO_Y + 10, SHERLOCK_SCREEN_WIDTH, STATUS_INFO_Y + 48));
color = (playerNum == 0) ? PLAYER_COLOR : DART_COL_FORE;
@@ -292,7 +292,7 @@ int Darts::throwDart(int dartNum, int computer) {
if (_vm->shouldQuit())
return 0;
- screen._backBuffer1.blitFrom(screen._backBuffer2, Common::Point(DART_INFO_X, DART_INFO_Y - 1),
+ screen._backBuffer1.SHblitFrom(screen._backBuffer2, Common::Point(DART_INFO_X, DART_INFO_Y - 1),
Common::Rect(DART_INFO_X, DART_INFO_Y - 1, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT));
screen.slamRect(Common::Rect(DART_INFO_X, DART_INFO_Y - 1, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT));
@@ -309,9 +309,9 @@ int Darts::throwDart(int dartNum, int computer) {
// Copy the bars to the secondary back buffer so that they remain fixed at their selected values
// whilst the dart is being animated at being thrown at the board
- screen._backBuffer2.blitFrom(screen._backBuffer1, Common::Point(DARTBARHX - 1, DARTHORIZY - 1),
+ screen._backBuffer2.SHblitFrom(screen._backBuffer1, Common::Point(DARTBARHX - 1, DARTHORIZY - 1),
Common::Rect(DARTBARHX - 1, DARTHORIZY - 1, DARTBARHX + DARTBARSIZE + 3, DARTHORIZY + 10));
- screen._backBuffer2.blitFrom(screen._backBuffer1, Common::Point(DARTBARVX - 1, DARTHEIGHTY - 1),
+ screen._backBuffer2.SHblitFrom(screen._backBuffer1, Common::Point(DARTBARVX - 1, DARTHEIGHTY - 1),
Common::Rect(DARTBARVX - 1, DARTHEIGHTY - 1, DARTBARVX + 11, DARTHEIGHTY + DARTBARSIZE + 3));
// Convert height and width to relative range of -50 to 50, where 0,0 is the exact centre of the board
@@ -344,7 +344,7 @@ void Darts::drawDartThrow(const Common::Point &pt) {
// Draw the dart
Common::Point drawPos(pos.x - frame._width / 2, pos.y - frame._height);
- screen._backBuffer1.transBlitFrom(frame, drawPos);
+ screen._backBuffer1.SHtransBlitFrom(frame, drawPos);
screen.slamArea(drawPos.x, drawPos.y, frame._width, frame._height);
// Handle erasing old dart frame area
@@ -352,14 +352,14 @@ void Darts::drawDartThrow(const Common::Point &pt) {
screen.slamRect(oldDrawBounds);
oldDrawBounds = Common::Rect(drawPos.x, drawPos.y, drawPos.x + frame._width, drawPos.y + frame._height);
- screen._backBuffer1.blitFrom(screen._backBuffer2, drawPos, oldDrawBounds);
+ screen._backBuffer1.SHblitFrom(screen._backBuffer2, drawPos, oldDrawBounds);
events.wait(2);
}
// Draw dart in final "stuck to board" form
- screen._backBuffer1.transBlitFrom((*_dartImages)[22], Common::Point(oldDrawBounds.left, oldDrawBounds.top));
- screen._backBuffer2.transBlitFrom((*_dartImages)[22], Common::Point(oldDrawBounds.left, oldDrawBounds.top));
+ screen._backBuffer1.SHtransBlitFrom((*_dartImages)[22], Common::Point(oldDrawBounds.left, oldDrawBounds.top));
+ screen._backBuffer2.SHtransBlitFrom((*_dartImages)[22], Common::Point(oldDrawBounds.left, oldDrawBounds.top));
screen.slamRect(oldDrawBounds);
}
@@ -368,8 +368,8 @@ void Darts::erasePowerBars() {
screen._backBuffer1.fillRect(Common::Rect(DARTBARHX, DARTHORIZY, DARTBARHX + DARTBARSIZE, DARTHORIZY + 10), BLACK);
screen._backBuffer1.fillRect(Common::Rect(DARTBARVX, DARTHEIGHTY, DARTBARVX + 10, DARTHEIGHTY + DARTBARSIZE), BLACK);
- screen._backBuffer1.transBlitFrom((*_dartImages)[2], Common::Point(DARTBARHX - 1, DARTHORIZY - 1));
- screen._backBuffer1.transBlitFrom((*_dartImages)[3], Common::Point(DARTBARVX - 1, DARTHEIGHTY - 1));
+ screen._backBuffer1.SHtransBlitFrom((*_dartImages)[2], Common::Point(DARTBARHX - 1, DARTHORIZY - 1));
+ screen._backBuffer1.SHtransBlitFrom((*_dartImages)[3], Common::Point(DARTBARVX - 1, DARTHEIGHTY - 1));
screen.slamArea(DARTBARHX - 1, DARTHORIZY - 1, DARTBARSIZE + 3, 11);
screen.slamArea(DARTBARVX - 1, DARTHEIGHTY - 1, 11, DARTBARSIZE + 3);
}
@@ -398,11 +398,11 @@ int Darts::doPowerBar(const Common::Point &pt, byte color, int goToPower, bool i
if (isVertical) {
screen._backBuffer1.hLine(pt.x, pt.y + DARTBARSIZE - 1 - idx, pt.x + 8, color);
- screen._backBuffer1.transBlitFrom((*_dartImages)[3], Common::Point(pt.x - 1, pt.y - 1));
+ screen._backBuffer1.SHtransBlitFrom((*_dartImages)[3], Common::Point(pt.x - 1, pt.y - 1));
screen.slamArea(pt.x, pt.y + DARTBARSIZE - 1 - idx, 8, 2);
} else {
screen._backBuffer1.vLine(pt.x + idx, pt.y, pt.y + 8, color);
- screen._backBuffer1.transBlitFrom((*_dartImages)[2], Common::Point(pt.x - 1, pt.y - 1));
+ screen._backBuffer1.SHtransBlitFrom((*_dartImages)[2], Common::Point(pt.x - 1, pt.y - 1));
screen.slamArea(pt.x + idx, pt.y, 1, 8);
}
diff --git a/engines/sherlock/scalpel/scalpel_fixed_text.cpp b/engines/sherlock/scalpel/scalpel_fixed_text.cpp
index 8932fb29c5..d76136a1db 100644
--- a/engines/sherlock/scalpel/scalpel_fixed_text.cpp
+++ b/engines/sherlock/scalpel/scalpel_fixed_text.cpp
@@ -105,6 +105,14 @@ static const char *const fixedTextEN[] = {
"Tarot Cards",
"An ornate key",
"A pawn ticket",
+ // SH1: User Interface
+ "No, thank you.",
+ "You can't do that.",
+ "Done...",
+ "Use ",
+ " on %s",
+ "Give ",
+ " to %s",
// SH1: People names
"Sherlock Holmes",
"Dr. Watson",
@@ -250,6 +258,14 @@ static const char *const fixedTextDE[] = {
"Ein Tarot-Kartenspiel", // original interpreter: "Ein Tarock-Kartenspiel" [sic]
"Ein verzierter Schl\201ssel",
"Ein Pfandschein",
+ // SH1: User Interface
+ "Nein, vielen Dank.",
+ "Nein, das geht wirklich nicht.", // original: "Nein, das geht wirklich nicht"
+ "Fertig...",
+ "Benutze ",
+ " mit %s",
+ "Gib ", // original: "Gebe "
+ " an %s", // original: " zu %s"
// SH1: People names
"Sherlock Holmes",
"Dr. Watson",
@@ -316,6 +332,7 @@ static const char *const fixedTextDE[] = {
// up-side down exclamation mark - 0xAD / octal 255
// up-side down question mark - 0xA8 / octal 250
// n with a wave on top - 0xA4 / octal 244
+// more characters see engines/sherlock/fixed_text.cpp
static const char *const fixedTextES[] = {
// Game hotkeys
"VMHTACIUDNFO",
@@ -394,6 +411,14 @@ static const char *const fixedTextES[] = {
"Unas cartas de Tarot",
"Una llave muy vistosa",
"Una papeleta de empe\244o",
+ // SH1: User Interface
+ "No, gracias.",
+ "No puedes hacerlo.", // original: "No puedes hacerlo"
+ "Hecho...",
+ "Usar ",
+ " sobre %s",
+ "Dar ",
+ " a %s",
// SH1: People names
"Sherlock Holmes",
"Dr. Watson",
@@ -517,7 +542,7 @@ static const char *const fixedTextDE_ActionMove[] = {
"L\204\341t sich nicht bewegen",
"Festged\201belt in der Erde...",
"Oha, VIEL zu schwer",
- "Der andere Kiste ist im Weg" // [sic]
+ "Die andere Kiste ist im Weg" // original: "Der andere Kiste ist im Weg"
};
static const char *const fixedTextES_ActionMove[] = {
@@ -616,8 +641,6 @@ static const FixedTextActionEntry fixedTextES_Actions[] = {
// =========================================
-// TODO:
-// It seems there was a French version of Sherlock Holmes 2
static const FixedTextLanguageEntry fixedTextLanguages[] = {
{ Common::DE_DEU, fixedTextDE, fixedTextDE_Actions },
{ Common::ES_ESP, fixedTextES, fixedTextES_Actions },
diff --git a/engines/sherlock/scalpel/scalpel_fixed_text.h b/engines/sherlock/scalpel/scalpel_fixed_text.h
index 841d602408..d9b3bbed79 100644
--- a/engines/sherlock/scalpel/scalpel_fixed_text.h
+++ b/engines/sherlock/scalpel/scalpel_fixed_text.h
@@ -107,6 +107,14 @@ enum FixedTextId {
kFixedText_InitInventory_Tarot,
kFixedText_InitInventory_OrnateKey,
kFixedText_InitInventory_PawnTicket,
+ // SH1: User Interface
+ kFixedText_UserInterface_NoThankYou,
+ kFixedText_UserInterface_YouCantDoThat,
+ kFixedText_UserInterface_Done,
+ kFixedText_UserInterface_Use,
+ kFixedText_UserInterface_UseOn,
+ kFixedText_UserInterface_Give,
+ kFixedText_UserInterface_GiveTo,
// People names
kFixedText_People_SherlockHolmes,
kFixedText_People_DrWatson,
diff --git a/engines/sherlock/scalpel/scalpel_inventory.cpp b/engines/sherlock/scalpel/scalpel_inventory.cpp
index c3e20295fd..e8d4d3b934 100644
--- a/engines/sherlock/scalpel/scalpel_inventory.cpp
+++ b/engines/sherlock/scalpel/scalpel_inventory.cpp
@@ -201,7 +201,7 @@ void ScalpelInventory::highlight(int index, byte color) {
ImageFrame &frame = (*_invShapes[slot])[0];
bb.fillRect(Common::Rect(8 + slot * 52, 165, (slot + 1) * 52, 194), color);
- bb.transBlitFrom(frame, Common::Point(6 + slot * 52 + ((47 - frame._width) / 2),
+ bb.SHtransBlitFrom(frame, Common::Point(6 + slot * 52 + ((47 - frame._width) / 2),
163 + ((33 - frame._height) / 2)));
screen.slamArea(8 + slot * 52, 165, 44, 30);
}
@@ -217,12 +217,12 @@ void ScalpelInventory::refreshInv() {
ui._infoFlag = true;
ui.clearInfo();
- screen._backBuffer2.blitFrom(screen._backBuffer1, Common::Point(0, CONTROLS_Y),
+ screen._backBuffer2.SHblitFrom(screen._backBuffer1, Common::Point(0, CONTROLS_Y),
Common::Rect(0, CONTROLS_Y, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT));
ui.examine();
if (!talk._talkToAbort) {
- screen._backBuffer2.blitFrom((*ui._controlPanel)[0], Common::Point(0, CONTROLS_Y));
+ screen._backBuffer2.SHblitFrom((*ui._controlPanel)[0], Common::Point(0, CONTROLS_Y));
loadInv();
}
}
@@ -264,7 +264,7 @@ void ScalpelInventory::putInv(InvSlamMode slamIt) {
// Draw the item image
ImageFrame &frame = (*_invShapes[itemNum])[0];
- bb.transBlitFrom(frame, Common::Point(6 + itemNum * 52 + ((47 - frame._width) / 2),
+ bb.SHtransBlitFrom(frame, Common::Point(6 + itemNum * 52 + ((47 - frame._width) / 2),
163 + ((33 - frame._height) / 2)));
}
diff --git a/engines/sherlock/scalpel/scalpel_map.cpp b/engines/sherlock/scalpel/scalpel_map.cpp
index 0924581e38..ba14b5b300 100644
--- a/engines/sherlock/scalpel/scalpel_map.cpp
+++ b/engines/sherlock/scalpel/scalpel_map.cpp
@@ -167,13 +167,13 @@ int ScalpelMap::show() {
setupSprites();
if (!IS_3DO) {
- screen._backBuffer1.blitFrom((*bigMap)[0], Common::Point(-_bigPos.x, -_bigPos.y));
- screen._backBuffer1.blitFrom((*bigMap)[1], Common::Point(-_bigPos.x, SHERLOCK_SCREEN_HEIGHT - _bigPos.y));
- screen._backBuffer1.blitFrom((*bigMap)[2], Common::Point(SHERLOCK_SCREEN_WIDTH - _bigPos.x, -_bigPos.y));
- screen._backBuffer1.blitFrom((*bigMap)[3], Common::Point(SHERLOCK_SCREEN_WIDTH - _bigPos.x, SHERLOCK_SCREEN_HEIGHT - _bigPos.y));
+ screen._backBuffer1.SHblitFrom((*bigMap)[0], Common::Point(-_bigPos.x, -_bigPos.y));
+ screen._backBuffer1.SHblitFrom((*bigMap)[1], Common::Point(-_bigPos.x, SHERLOCK_SCREEN_HEIGHT - _bigPos.y));
+ screen._backBuffer1.SHblitFrom((*bigMap)[2], Common::Point(SHERLOCK_SCREEN_WIDTH - _bigPos.x, -_bigPos.y));
+ screen._backBuffer1.SHblitFrom((*bigMap)[3], Common::Point(SHERLOCK_SCREEN_WIDTH - _bigPos.x, SHERLOCK_SCREEN_HEIGHT - _bigPos.y));
} else {
- screen._backBuffer1.blitFrom((*bigMap)[0], Common::Point(-_bigPos.x, -_bigPos.y));
- screen.blitFrom((*bigMap)[0], Common::Point(-_bigPos.x, -_bigPos.y));
+ screen._backBuffer1.SHblitFrom((*bigMap)[0], Common::Point(-_bigPos.x, -_bigPos.y));
+ screen.SHblitFrom((*bigMap)[0], Common::Point(-_bigPos.x, -_bigPos.y));
}
_drawMap = true;
@@ -238,12 +238,12 @@ int ScalpelMap::show() {
changed = false;
if (!IS_3DO) {
- screen._backBuffer1.blitFrom((*bigMap)[0], Common::Point(-_bigPos.x, -_bigPos.y));
- screen._backBuffer1.blitFrom((*bigMap)[1], Common::Point(-_bigPos.x, SHERLOCK_SCREEN_HEIGHT - _bigPos.y));
- screen._backBuffer1.blitFrom((*bigMap)[2], Common::Point(SHERLOCK_SCREEN_WIDTH - _bigPos.x, -_bigPos.y));
- screen._backBuffer1.blitFrom((*bigMap)[3], Common::Point(SHERLOCK_SCREEN_WIDTH - _bigPos.x, SHERLOCK_SCREEN_HEIGHT - _bigPos.y));
+ screen._backBuffer1.SHblitFrom((*bigMap)[0], Common::Point(-_bigPos.x, -_bigPos.y));
+ screen._backBuffer1.SHblitFrom((*bigMap)[1], Common::Point(-_bigPos.x, SHERLOCK_SCREEN_HEIGHT - _bigPos.y));
+ screen._backBuffer1.SHblitFrom((*bigMap)[2], Common::Point(SHERLOCK_SCREEN_WIDTH - _bigPos.x, -_bigPos.y));
+ screen._backBuffer1.SHblitFrom((*bigMap)[3], Common::Point(SHERLOCK_SCREEN_WIDTH - _bigPos.x, SHERLOCK_SCREEN_HEIGHT - _bigPos.y));
} else {
- screen._backBuffer1.blitFrom((*bigMap)[0], Common::Point(-_bigPos.x, -_bigPos.y));
+ screen._backBuffer1.SHblitFrom((*bigMap)[0], Common::Point(-_bigPos.x, -_bigPos.y));
}
showPlaces();
@@ -359,7 +359,6 @@ void ScalpelMap::freeSprites() {
delete _mapCursors;
delete _shapes;
delete _iconShapes;
- _iconSave.free();
}
void ScalpelMap::showPlaces() {
@@ -376,7 +375,7 @@ void ScalpelMap::showPlaces() {
if (pt.x >= _bigPos.x && (pt.x - _bigPos.x) < SHERLOCK_SCREEN_WIDTH
&& pt.y >= _bigPos.y && (pt.y - _bigPos.y) < SHERLOCK_SCREEN_HEIGHT) {
if (_vm->readFlags(idx)) {
- screen._backBuffer1.transBlitFrom((*_iconShapes)[pt._translate],
+ screen._backBuffer1.SHtransBlitFrom((*_iconShapes)[pt._translate],
Common::Point(pt.x - _bigPos.x - 6, pt.y - _bigPos.y - 12));
}
}
@@ -388,13 +387,13 @@ void ScalpelMap::showPlaces() {
}
void ScalpelMap::saveTopLine() {
- _topLine.blitFrom(_vm->_screen->_backBuffer1, Common::Point(0, 0), Common::Rect(0, 0, SHERLOCK_SCREEN_WIDTH, 12));
+ _topLine.SHblitFrom(_vm->_screen->_backBuffer1, Common::Point(0, 0), Common::Rect(0, 0, SHERLOCK_SCREEN_WIDTH, 12));
}
void ScalpelMap::eraseTopLine() {
Screen &screen = *_vm->_screen;
- screen._backBuffer1.blitFrom(_topLine, Common::Point(0, 0));
- screen.slamArea(0, 0, SHERLOCK_SCREEN_WIDTH, _topLine.h());
+ screen._backBuffer1.SHblitFrom(_topLine, Common::Point(0, 0));
+ screen.slamArea(0, 0, SHERLOCK_SCREEN_WIDTH, _topLine.height());
}
void ScalpelMap::showPlaceName(int idx, bool highlighted) {
@@ -409,7 +408,7 @@ void ScalpelMap::showPlaceName(int idx, bool highlighted) {
bool flipped = people[HOLMES]._sequenceNumber == MAP_DOWNLEFT || people[HOLMES]._sequenceNumber == MAP_LEFT
|| people[HOLMES]._sequenceNumber == MAP_UPLEFT;
- screen._backBuffer1.transBlitFrom(*people[HOLMES]._imageFrame, _lDrawnPos, flipped);
+ screen._backBuffer1.SHtransBlitFrom(*people[HOLMES]._imageFrame, _lDrawnPos, flipped);
}
if (highlighted) {
@@ -451,9 +450,9 @@ void ScalpelMap::updateMap(bool flushScreen) {
saveIcon(people[HOLMES]._imageFrame, hPos);
if (people[HOLMES]._sequenceNumber == MAP_DOWNLEFT || people[HOLMES]._sequenceNumber == MAP_LEFT
|| people[HOLMES]._sequenceNumber == MAP_UPLEFT)
- screen._backBuffer1.transBlitFrom(*people[HOLMES]._imageFrame, hPos, true);
+ screen._backBuffer1.SHtransBlitFrom(*people[HOLMES]._imageFrame, hPos, true);
else
- screen._backBuffer1.transBlitFrom(*people[HOLMES]._imageFrame, hPos, false);
+ screen._backBuffer1.SHtransBlitFrom(*people[HOLMES]._imageFrame, hPos, false);
if (flushScreen) {
screen.slamArea(0, 0, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT);
@@ -553,8 +552,8 @@ void ScalpelMap::saveIcon(ImageFrame *src, const Common::Point &pt) {
return;
}
- assert(size.x <= _iconSave.w() && size.y <= _iconSave.h());
- _iconSave.blitFrom(screen._backBuffer1, Common::Point(0, 0),
+ assert(size.x <= _iconSave.width() && size.y <= _iconSave.height());
+ _iconSave.SHblitFrom(screen._backBuffer1, Common::Point(0, 0),
Common::Rect(pos.x, pos.y, pos.x + size.x, pos.y + size.y));
_savedPos = pos;
_savedSize = size;
@@ -565,7 +564,7 @@ void ScalpelMap::restoreIcon() {
if (_savedPos.x >= 0 && _savedPos.y >= 0 && _savedPos.x <= SHERLOCK_SCREEN_WIDTH
&& _savedPos.y < SHERLOCK_SCREEN_HEIGHT)
- screen._backBuffer1.blitFrom(_iconSave, _savedPos, Common::Rect(0, 0, _savedSize.x, _savedSize.y));
+ screen._backBuffer1.SHblitFrom(_iconSave, _savedPos, Common::Rect(0, 0, _savedSize.x, _savedSize.y));
}
void ScalpelMap::highlightIcon(const Common::Point &pt) {
diff --git a/engines/sherlock/scalpel/scalpel_scene.cpp b/engines/sherlock/scalpel/scalpel_scene.cpp
index b62703e0fb..83e49bb3fa 100644
--- a/engines/sherlock/scalpel/scalpel_scene.cpp
+++ b/engines/sherlock/scalpel/scalpel_scene.cpp
@@ -71,26 +71,26 @@ void ScalpelScene::drawAllShapes() {
// Draw all active shapes which are behind the person
for (uint idx = 0; idx < _bgShapes.size(); ++idx) {
if (_bgShapes[idx]._type == ACTIVE_BG_SHAPE && _bgShapes[idx]._misc == BEHIND)
- screen._backBuffer->transBlitFrom(*_bgShapes[idx]._imageFrame, _bgShapes[idx]._position, _bgShapes[idx]._flags & OBJ_FLIPPED);
+ screen._backBuffer->SHtransBlitFrom(*_bgShapes[idx]._imageFrame, _bgShapes[idx]._position, _bgShapes[idx]._flags & OBJ_FLIPPED);
}
// Draw all canimations which are behind the person
for (uint idx = 0; idx < _canimShapes.size(); ++idx) {
if (_canimShapes[idx]->_type == ACTIVE_BG_SHAPE && _canimShapes[idx]->_misc == BEHIND)
- screen._backBuffer->transBlitFrom(*_canimShapes[idx]->_imageFrame,
+ screen._backBuffer->SHtransBlitFrom(*_canimShapes[idx]->_imageFrame,
_canimShapes[idx]->_position, _canimShapes[idx]->_flags & OBJ_FLIPPED);
}
// Draw all active shapes which are normal and behind the person
for (uint idx = 0; idx < _bgShapes.size(); ++idx) {
if (_bgShapes[idx]._type == ACTIVE_BG_SHAPE && _bgShapes[idx]._misc == NORMAL_BEHIND)
- screen._backBuffer->transBlitFrom(*_bgShapes[idx]._imageFrame, _bgShapes[idx]._position, _bgShapes[idx]._flags & OBJ_FLIPPED);
+ screen._backBuffer->SHtransBlitFrom(*_bgShapes[idx]._imageFrame, _bgShapes[idx]._position, _bgShapes[idx]._flags & OBJ_FLIPPED);
}
// Draw all canimations which are normal and behind the person
for (uint idx = 0; idx < _canimShapes.size(); ++idx) {
if (_canimShapes[idx]->_type == ACTIVE_BG_SHAPE && _canimShapes[idx]->_misc == NORMAL_BEHIND)
- screen._backBuffer->transBlitFrom(*_canimShapes[idx]->_imageFrame, _canimShapes[idx]->_position,
+ screen._backBuffer->SHtransBlitFrom(*_canimShapes[idx]->_imageFrame, _canimShapes[idx]->_position,
_canimShapes[idx]->_flags & OBJ_FLIPPED);
}
@@ -103,7 +103,7 @@ void ScalpelScene::drawAllShapes() {
p._sequenceNumber == WALK_UPLEFT || p._sequenceNumber == STOP_UPLEFT ||
p._sequenceNumber == WALK_DOWNRIGHT || p._sequenceNumber == STOP_DOWNRIGHT);
- screen._backBuffer->transBlitFrom(*p._imageFrame, Common::Point(p._position.x / FIXED_INT_MULTIPLIER,
+ screen._backBuffer->SHtransBlitFrom(*p._imageFrame, Common::Point(p._position.x / FIXED_INT_MULTIPLIER,
p._position.y / FIXED_INT_MULTIPLIER - p.frameHeight()), flipped);
}
}
@@ -112,7 +112,7 @@ void ScalpelScene::drawAllShapes() {
for (uint idx = 0; idx < _bgShapes.size(); ++idx) {
if ((_bgShapes[idx]._type == ACTIVE_BG_SHAPE || _bgShapes[idx]._type == STATIC_BG_SHAPE) &&
_bgShapes[idx]._misc == NORMAL_FORWARD)
- screen._backBuffer->transBlitFrom(*_bgShapes[idx]._imageFrame, _bgShapes[idx]._position,
+ screen._backBuffer->SHtransBlitFrom(*_bgShapes[idx]._imageFrame, _bgShapes[idx]._position,
_bgShapes[idx]._flags & OBJ_FLIPPED);
}
@@ -120,7 +120,7 @@ void ScalpelScene::drawAllShapes() {
for (uint idx = 0; idx < _canimShapes.size(); ++idx) {
if ((_canimShapes[idx]->_type == ACTIVE_BG_SHAPE || _canimShapes[idx]->_type == STATIC_BG_SHAPE) &&
_canimShapes[idx]->_misc == NORMAL_FORWARD)
- screen._backBuffer->transBlitFrom(*_canimShapes[idx]->_imageFrame, _canimShapes[idx]->_position,
+ screen._backBuffer->SHtransBlitFrom(*_canimShapes[idx]->_imageFrame, _canimShapes[idx]->_position,
_canimShapes[idx]->_flags & OBJ_FLIPPED);
}
@@ -132,7 +132,7 @@ void ScalpelScene::drawAllShapes() {
if ((_bgShapes[idx]._type == ACTIVE_BG_SHAPE || _bgShapes[idx]._type == STATIC_BG_SHAPE) &&
_bgShapes[idx]._misc == FORWARD)
- screen._backBuffer->transBlitFrom(*_bgShapes[idx]._imageFrame, _bgShapes[idx]._position,
+ screen._backBuffer->SHtransBlitFrom(*_bgShapes[idx]._imageFrame, _bgShapes[idx]._position,
_bgShapes[idx]._flags & OBJ_FLIPPED);
}
@@ -140,7 +140,7 @@ void ScalpelScene::drawAllShapes() {
for (uint idx = 0; idx < _canimShapes.size(); ++idx) {
if ((_canimShapes[idx]->_type == ACTIVE_BG_SHAPE || _canimShapes[idx]->_type == STATIC_BG_SHAPE) &&
_canimShapes[idx]->_misc == FORWARD)
- screen._backBuffer->transBlitFrom(*_canimShapes[idx]->_imageFrame, _canimShapes[idx]->_position,
+ screen._backBuffer->SHtransBlitFrom(*_canimShapes[idx]->_imageFrame, _canimShapes[idx]->_position,
_canimShapes[idx]->_flags & OBJ_FLIPPED);
}
@@ -242,7 +242,7 @@ void ScalpelScene::doBgAnim() {
if (people[HOLMES]._type == CHARACTER)
screen.restoreBackground(bounds);
else if (people[HOLMES]._type == REMOVE)
- screen._backBuffer->blitFrom(screen._backBuffer2, pt, bounds);
+ screen._backBuffer->SHblitFrom(screen._backBuffer2, pt, bounds);
for (uint idx = 0; idx < _bgShapes.size(); ++idx) {
Object &o = _bgShapes[idx];
@@ -261,7 +261,7 @@ void ScalpelScene::doBgAnim() {
Object &o = _bgShapes[idx];
if (o._type == NO_SHAPE && ((o._flags & OBJ_BEHIND) == 0)) {
// Restore screen area
- screen._backBuffer->blitFrom(screen._backBuffer2, o._position,
+ screen._backBuffer->SHblitFrom(screen._backBuffer2, o._position,
Common::Rect(o._position.x, o._position.y,
o._position.x + o._noShapeSize.x, o._position.y + o._noShapeSize.y));
@@ -309,14 +309,14 @@ void ScalpelScene::doBgAnim() {
for (uint idx = 0; idx < _bgShapes.size(); ++idx) {
Object &o = _bgShapes[idx];
if (o._type == ACTIVE_BG_SHAPE && o._misc == BEHIND)
- screen._backBuffer->transBlitFrom(*o._imageFrame, o._position, o._flags & OBJ_FLIPPED);
+ screen._backBuffer->SHtransBlitFrom(*o._imageFrame, o._position, o._flags & OBJ_FLIPPED);
}
// Draw all canimations which are behind the person
for (uint idx = 0; idx < _canimShapes.size(); ++idx) {
Object &o = *_canimShapes[idx];
if (o._type == ACTIVE_BG_SHAPE && o._misc == BEHIND) {
- screen._backBuffer->transBlitFrom(*o._imageFrame, o._position, o._flags & OBJ_FLIPPED);
+ screen._backBuffer->SHtransBlitFrom(*o._imageFrame, o._position, o._flags & OBJ_FLIPPED);
}
}
@@ -324,14 +324,14 @@ void ScalpelScene::doBgAnim() {
for (uint idx = 0; idx < _bgShapes.size(); ++idx) {
Object &o = _bgShapes[idx];
if (o._type == ACTIVE_BG_SHAPE && o._misc == NORMAL_BEHIND)
- screen._backBuffer->transBlitFrom(*o._imageFrame, o._position, o._flags & OBJ_FLIPPED);
+ screen._backBuffer->SHtransBlitFrom(*o._imageFrame, o._position, o._flags & OBJ_FLIPPED);
}
// Draw all canimations which are NORMAL and behind the person
for (uint idx = 0; idx < _canimShapes.size(); ++idx) {
Object &o = *_canimShapes[idx];
if (o._type == ACTIVE_BG_SHAPE && o._misc == NORMAL_BEHIND) {
- screen._backBuffer->transBlitFrom(*o._imageFrame, o._position, o._flags & OBJ_FLIPPED);
+ screen._backBuffer->SHtransBlitFrom(*o._imageFrame, o._position, o._flags & OBJ_FLIPPED);
}
}
@@ -344,7 +344,7 @@ void ScalpelScene::doBgAnim() {
bool flipped = people[HOLMES]._sequenceNumber == WALK_LEFT || people[HOLMES]._sequenceNumber == STOP_LEFT ||
people[HOLMES]._sequenceNumber == WALK_UPLEFT || people[HOLMES]._sequenceNumber == STOP_UPLEFT ||
people[HOLMES]._sequenceNumber == WALK_DOWNRIGHT || people[HOLMES]._sequenceNumber == STOP_DOWNRIGHT;
- screen._backBuffer->transBlitFrom(*people[HOLMES]._imageFrame,
+ screen._backBuffer->SHtransBlitFrom(*people[HOLMES]._imageFrame,
Common::Point(tempX, people[HOLMES]._position.y / FIXED_INT_MULTIPLIER - people[HOLMES]._imageFrame->_frame.h), flipped);
}
@@ -352,14 +352,14 @@ void ScalpelScene::doBgAnim() {
for (uint idx = 0; idx < _bgShapes.size(); ++idx) {
Object &o = _bgShapes[idx];
if ((o._type == ACTIVE_BG_SHAPE || o._type == STATIC_BG_SHAPE) && o._misc == NORMAL_FORWARD)
- screen._backBuffer->transBlitFrom(*o._imageFrame, o._position, o._flags & OBJ_FLIPPED);
+ screen._backBuffer->SHtransBlitFrom(*o._imageFrame, o._position, o._flags & OBJ_FLIPPED);
}
// Draw all static and active canimations that are NORMAL and are in front of the person
for (uint idx = 0; idx < _canimShapes.size(); ++idx) {
Object &o = *_canimShapes[idx];
if ((o._type == ACTIVE_BG_SHAPE || o._type == STATIC_BG_SHAPE) && o._misc == NORMAL_FORWARD) {
- screen._backBuffer->transBlitFrom(*o._imageFrame, o._position, o._flags & OBJ_FLIPPED);
+ screen._backBuffer->SHtransBlitFrom(*o._imageFrame, o._position, o._flags & OBJ_FLIPPED);
}
}
@@ -367,19 +367,19 @@ void ScalpelScene::doBgAnim() {
for (uint idx = 0; idx < _bgShapes.size(); ++idx) {
Object &o = _bgShapes[idx];
if ((o._type == ACTIVE_BG_SHAPE || o._type == STATIC_BG_SHAPE) && o._misc == FORWARD)
- screen._backBuffer->transBlitFrom(*o._imageFrame, o._position, o._flags & OBJ_FLIPPED);
+ screen._backBuffer->SHtransBlitFrom(*o._imageFrame, o._position, o._flags & OBJ_FLIPPED);
}
// Draw any active portrait
if (people._portraitLoaded && people._portrait._type == ACTIVE_BG_SHAPE)
- screen._backBuffer->transBlitFrom(*people._portrait._imageFrame,
+ screen._backBuffer->SHtransBlitFrom(*people._portrait._imageFrame,
people._portrait._position, people._portrait._flags & OBJ_FLIPPED);
// Draw all static and active canimations that are in front of the person
for (uint idx = 0; idx < _canimShapes.size(); ++idx) {
Object &o = *_canimShapes[idx];
if ((o._type == ACTIVE_BG_SHAPE || o._type == STATIC_BG_SHAPE) && o._misc == FORWARD) {
- screen._backBuffer->transBlitFrom(*o._imageFrame, o._position, o._flags & OBJ_FLIPPED);
+ screen._backBuffer->SHtransBlitFrom(*o._imageFrame, o._position, o._flags & OBJ_FLIPPED);
}
}
@@ -387,7 +387,7 @@ void ScalpelScene::doBgAnim() {
for (uint idx = 0; idx < _bgShapes.size(); ++idx) {
Object &o = _bgShapes[idx];
if (o._type == NO_SHAPE && (o._flags & OBJ_BEHIND) == 0)
- screen._backBuffer->transBlitFrom(*o._imageFrame, o._position, o._flags & OBJ_FLIPPED);
+ screen._backBuffer->SHtransBlitFrom(*o._imageFrame, o._position, o._flags & OBJ_FLIPPED);
}
// Bring the newly built picture to the screen
diff --git a/engines/sherlock/scalpel/scalpel_screen.cpp b/engines/sherlock/scalpel/scalpel_screen.cpp
index 197a2a2634..37e5294c02 100644
--- a/engines/sherlock/scalpel/scalpel_screen.cpp
+++ b/engines/sherlock/scalpel/scalpel_screen.cpp
@@ -28,6 +28,8 @@ namespace Sherlock {
namespace Scalpel {
ScalpelScreen::ScalpelScreen(SherlockEngine *vm) : Screen(vm) {
+ _backBuffer1.create(320, 200);
+ _backBuffer2.create(320, 200);
}
void ScalpelScreen::makeButton(const Common::Rect &bounds, int textX,
@@ -123,255 +125,6 @@ void ScalpelScreen::makeField(const Common::Rect &r) {
_backBuffer->vLine(r.right - 1, r.top + 1, r.bottom - 2, BUTTON_TOP);
}
-/*----------------------------------------------------------------*/
-
-void Scalpel3DOScreen::blitFrom(const Graphics::Surface &src, const Common::Point &pt, const Common::Rect &srcBounds) {
- if (!_vm->_isScreenDoubled) {
- ScalpelScreen::blitFrom(src, pt, srcBounds);
- return;
- }
-
- Common::Rect srcRect = srcBounds;
- Common::Rect destRect(pt.x, pt.y, pt.x + srcRect.width(), pt.y + srcRect.height());
-
- if (!srcRect.isValidRect() || !clip(srcRect, destRect))
- return;
-
- // Add dirty area remapped to the 640x200 surface
- addDirtyRect(Common::Rect(destRect.left * 2, destRect.top * 2, destRect.right * 2, destRect.bottom * 2));
-
- // Transfer the area, doubling each pixel
- for (int yp = 0; yp < srcRect.height(); ++yp) {
- const uint16 *srcP = (const uint16 *)src.getBasePtr(srcRect.left, srcRect.top + yp);
- uint16 *destP = (uint16 *)getBasePtr(destRect.left * 2, (destRect.top + yp) * 2);
-
- for (int xp = srcRect.left; xp < srcRect.right; ++xp, ++srcP, destP += 2) {
- *destP = *srcP;
- *(destP + 1) = *srcP;
- *(destP + 640) = *srcP;
- *(destP + 640 + 1) = *srcP;
- }
- }
-}
-
-void Scalpel3DOScreen::transBlitFromUnscaled(const Graphics::Surface &src, const Common::Point &pt,
- bool flipped, int overrideColor) {
- if (!_vm->_isScreenDoubled) {
- ScalpelScreen::transBlitFromUnscaled(src, pt, flipped, overrideColor);
- return;
- }
-
- Common::Rect drawRect(0, 0, src.w, src.h);
- Common::Rect destRect(pt.x, pt.y, pt.x + src.w, pt.y + src.h);
-
- // Clip the display area to on-screen
- if (!clip(drawRect, destRect))
- // It's completely off-screen
- return;
-
- if (flipped)
- drawRect = Common::Rect(src.w - drawRect.right, src.h - drawRect.bottom,
- src.w - drawRect.left, src.h - drawRect.top);
-
- Common::Point destPt(destRect.left, destRect.top);
- addDirtyRect(Common::Rect(destPt.x * 2, destPt.y * 2, (destPt.x + drawRect.width()) * 2,
- (destPt.y + drawRect.height()) * 2));
-
- assert(src.format.bytesPerPixel == 2 && _surface.format.bytesPerPixel == 2);
-
- for (int yp = 0; yp < drawRect.height(); ++yp) {
- const uint16 *srcP = (const uint16 *)src.getBasePtr(
- flipped ? drawRect.right - 1 : drawRect.left, drawRect.top + yp);
- uint16 *destP = (uint16 *)getBasePtr(destPt.x * 2, (destPt.y + yp) * 2);
-
- for (int xp = 0; xp < drawRect.width(); ++xp, destP += 2) {
- // RGB 0, 0, 0 -> transparent on 3DO
- if (*srcP) {
- *destP = *srcP;
- *(destP + 1) = *srcP;
- *(destP + 640) = *srcP;
- *(destP + 640 + 1) = *srcP;
- }
-
- srcP = flipped ? srcP - 1 : srcP + 1;
- }
- }
-}
-
-void Scalpel3DOScreen::fillRect(const Common::Rect &r, uint color) {
- if (_vm->_isScreenDoubled)
- ScalpelScreen::fillRect(Common::Rect(r.left * 2, r.top * 2, r.right * 2, r.bottom * 2), color);
- else
- ScalpelScreen::fillRect(r, color);
-}
-
-void Scalpel3DOScreen::fadeIntoScreen3DO(int speed) {
- Events &events = *_vm->_events;
- uint16 *currentScreenBasePtr = (uint16 *)getPixels();
- uint16 *targetScreenBasePtr = (uint16 *)_backBuffer->getPixels();
- uint16 currentScreenPixel = 0;
- uint16 targetScreenPixel = 0;
-
- uint16 currentScreenPixelRed = 0;
- uint16 currentScreenPixelGreen = 0;
- uint16 currentScreenPixelBlue = 0;
-
- uint16 targetScreenPixelRed = 0;
- uint16 targetScreenPixelGreen = 0;
- uint16 targetScreenPixelBlue = 0;
-
- uint16 screenWidth = SHERLOCK_SCREEN_WIDTH;
- uint16 screenHeight = SHERLOCK_SCREEN_HEIGHT;
- uint16 screenX = 0;
- uint16 screenY = 0;
- uint16 pixelsChanged = 0;
-
- clearDirtyRects();
-
- do {
- pixelsChanged = 0;
- uint16 *currentScreenPtr = currentScreenBasePtr;
- uint16 *targetScreenPtr = targetScreenBasePtr;
-
- for (screenY = 0; screenY < screenHeight; screenY++) {
- for (screenX = 0; screenX < screenWidth; screenX++) {
- currentScreenPixel = *currentScreenPtr;
- targetScreenPixel = *targetScreenPtr;
-
- if (currentScreenPixel != targetScreenPixel) {
- // pixel doesn't match, adjust accordingly
- currentScreenPixelRed = currentScreenPixel & 0xF800;
- currentScreenPixelGreen = currentScreenPixel & 0x07E0;
- currentScreenPixelBlue = currentScreenPixel & 0x001F;
- targetScreenPixelRed = targetScreenPixel & 0xF800;
- targetScreenPixelGreen = targetScreenPixel & 0x07E0;
- targetScreenPixelBlue = targetScreenPixel & 0x001F;
-
- if (currentScreenPixelRed != targetScreenPixelRed) {
- if (currentScreenPixelRed < targetScreenPixelRed) {
- currentScreenPixelRed += 0x0800;
- } else {
- currentScreenPixelRed -= 0x0800;
- }
- }
- if (currentScreenPixelGreen != targetScreenPixelGreen) {
- // Adjust +2/-2 because we are running RGB555 at RGB565
- if (currentScreenPixelGreen < targetScreenPixelGreen) {
- currentScreenPixelGreen += 0x0040;
- } else {
- currentScreenPixelGreen -= 0x0040;
- }
- }
- if (currentScreenPixelBlue != targetScreenPixelBlue) {
- if (currentScreenPixelBlue < targetScreenPixelBlue) {
- currentScreenPixelBlue += 0x0001;
- } else {
- currentScreenPixelBlue -= 0x0001;
- }
- }
-
- uint16 v = currentScreenPixelRed | currentScreenPixelGreen | currentScreenPixelBlue;
- *currentScreenPtr = v;
- if (_vm->_isScreenDoubled) {
- *(currentScreenPtr + 1) = v;
- *(currentScreenPtr + 640) = v;
- *(currentScreenPtr + 640 + 1) = v;
- }
-
- pixelsChanged++;
- }
-
- currentScreenPtr += _vm->_isScreenDoubled ? 2 : 1;
- targetScreenPtr++;
- }
-
- if (_vm->_isScreenDoubled)
- currentScreenPtr += 640;
- }
-
- // Too much considered dirty at the moment
- if (_vm->_isScreenDoubled)
- addDirtyRect(Common::Rect(0, 0, screenWidth * 2, screenHeight * 2));
- else
- addDirtyRect(Common::Rect(0, 0, screenWidth, screenHeight));
-
- events.pollEvents();
- events.delay(10 * speed);
- } while ((pixelsChanged) && (!_vm->shouldQuit()));
-}
-
-void Scalpel3DOScreen::blitFrom3DOcolorLimit(uint16 limitColor) {
- uint16 *currentScreenPtr = (uint16 *)getPixels();
- uint16 *targetScreenPtr = (uint16 *)_backBuffer->getPixels();
- uint16 currentScreenPixel = 0;
-
- uint16 screenWidth = SHERLOCK_SCREEN_WIDTH;
- uint16 screenHeight = SHERLOCK_SCREEN_HEIGHT;
- uint16 screenX = 0;
- uint16 screenY = 0;
-
- uint16 currentScreenPixelRed = 0;
- uint16 currentScreenPixelGreen = 0;
- uint16 currentScreenPixelBlue = 0;
-
- uint16 limitPixelRed = limitColor & 0xF800;
- uint16 limitPixelGreen = limitColor & 0x07E0;
- uint16 limitPixelBlue = limitColor & 0x001F;
-
- for (screenY = 0; screenY < screenHeight; screenY++) {
- for (screenX = 0; screenX < screenWidth; screenX++) {
- currentScreenPixel = *targetScreenPtr;
-
- currentScreenPixelRed = currentScreenPixel & 0xF800;
- currentScreenPixelGreen = currentScreenPixel & 0x07E0;
- currentScreenPixelBlue = currentScreenPixel & 0x001F;
-
- if (currentScreenPixelRed < limitPixelRed)
- currentScreenPixelRed = limitPixelRed;
- if (currentScreenPixelGreen < limitPixelGreen)
- currentScreenPixelGreen = limitPixelGreen;
- if (currentScreenPixelBlue < limitPixelBlue)
- currentScreenPixelBlue = limitPixelBlue;
-
- uint16 v = currentScreenPixelRed | currentScreenPixelGreen | currentScreenPixelBlue;
- *currentScreenPtr = v;
- if (_vm->_isScreenDoubled) {
- *(currentScreenPtr + 1) = v;
- *(currentScreenPtr + 640) = v;
- *(currentScreenPtr + 640 + 1) = v;
- }
-
- currentScreenPtr += _vm->_isScreenDoubled ? 2 : 1;
- targetScreenPtr++;
- }
-
- if (_vm->_isScreenDoubled)
- currentScreenPtr += 640;
- }
-
- // Too much considered dirty at the moment
- if (_vm->_isScreenDoubled)
- addDirtyRect(Common::Rect(0, 0, screenWidth * 2, screenHeight * 2));
- else
- addDirtyRect(Common::Rect(0, 0, screenWidth, screenHeight));
-}
-
-uint16 Scalpel3DOScreen::w() const {
- return _vm->_isScreenDoubled ? _surface.w / 2 : _surface.w;
-}
-
-uint16 Scalpel3DOScreen::h() const {
- return _vm->_isScreenDoubled ? _surface.h / 2 : _surface.h;
-}
-
-void Scalpel3DOScreen::rawBlitFrom(const Graphics::Surface &src, const Common::Point &pt) {
- Common::Rect srcRect(0, 0, src.w, src.h);
- Common::Rect destRect(pt.x, pt.y, pt.x + src.w, pt.y + src.h);
-
- addDirtyRect(destRect);
- _surface.copyRectToSurface(src, destRect.left, destRect.top, srcRect);
-}
-
} // End of namespace Scalpel
} // End of namespace Sherlock
diff --git a/engines/sherlock/scalpel/scalpel_screen.h b/engines/sherlock/scalpel/scalpel_screen.h
index cee33b8c6c..d9be29c8b2 100644
--- a/engines/sherlock/scalpel/scalpel_screen.h
+++ b/engines/sherlock/scalpel/scalpel_screen.h
@@ -61,44 +61,6 @@ public:
void makeField(const Common::Rect &r);
};
-class Scalpel3DOScreen : public ScalpelScreen {
-protected:
- /**
- * Draws a sub-section of a surface at a given position within this surface
- * Overriden for the 3DO to automatically double the size of everything to the underlying 640x400 surface
- */
- virtual void blitFrom(const Graphics::Surface &src, const Common::Point &pt, const Common::Rect &srcBounds);
-
- /**
- * Draws a surface at a given position within this surface with transparency
- */
- virtual void transBlitFromUnscaled(const Graphics::Surface &src, const Common::Point &pt, bool flipped,
- int overrideColor);
-public:
- Scalpel3DOScreen(SherlockEngine *vm) : ScalpelScreen(vm) {}
- virtual ~Scalpel3DOScreen() {}
-
- /**
- * Draws a sub-section of a surface at a given position within this surface
- */
- void rawBlitFrom(const Graphics::Surface &src, const Common::Point &pt);
-
- /**
- * Fade backbuffer 1 into screen (3DO RGB!)
- */
- void fadeIntoScreen3DO(int speed);
-
- void blitFrom3DOcolorLimit(uint16 color);
-
- /**
- * Fill a given area of the surface with a given color
- */
- virtual void fillRect(const Common::Rect &r, uint color);
-
- virtual uint16 w() const;
- virtual uint16 h() const;
-};
-
} // End of namespace Scalpel
} // End of namespace Sherlock
diff --git a/engines/sherlock/scalpel/scalpel_user_interface.cpp b/engines/sherlock/scalpel/scalpel_user_interface.cpp
index 4e7cf5c378..6534f61a87 100644
--- a/engines/sherlock/scalpel/scalpel_user_interface.cpp
+++ b/engines/sherlock/scalpel/scalpel_user_interface.cpp
@@ -148,23 +148,24 @@ void ScalpelUserInterface::reset() {
void ScalpelUserInterface::drawInterface(int bufferNum) {
Screen &screen = *_vm->_screen;
- const ImageFrame &src = (*_controlPanel)[0];
+ const Graphics::Surface &src = (*_controlPanel)[0]._frame;
int16 x = (!IS_3DO) ? 0 : UI_OFFSET_3DO;
if (bufferNum & 1) {
if (IS_3DO)
screen._backBuffer1.fillRect(Common::Rect(0, CONTROLS_Y,
SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT), BLACK);
- screen._backBuffer1.transBlitFrom(src, Common::Point(x, CONTROLS_Y));
+ screen._backBuffer1.SHtransBlitFrom(src, Common::Point(x, CONTROLS_Y));
}
if (bufferNum & 2) {
if (IS_3DO)
screen._backBuffer2.fillRect(Common::Rect(0, CONTROLS_Y,
SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT), BLACK);
- screen._backBuffer2.transBlitFrom(src, Common::Point(x, CONTROLS_Y));
+ screen._backBuffer2.SHtransBlitFrom(src, Common::Point(x, CONTROLS_Y));
}
if (bufferNum == 3)
- screen._backBuffer2.fillRect(0, INFO_LINE, SHERLOCK_SCREEN_WIDTH, INFO_LINE + 10, INFO_BLACK);
+ screen._backBuffer2.SHfillRect(Common::Rect(0, INFO_LINE,
+ SHERLOCK_SCREEN_WIDTH, INFO_LINE + 10), INFO_BLACK);
}
void ScalpelUserInterface::handleInput() {
@@ -426,7 +427,7 @@ void ScalpelUserInterface::depressButton(int num) {
offsetButton3DO(pt, num);
ImageFrame &frame = (*_controls)[num];
- screen._backBuffer1.transBlitFrom(frame, pt);
+ screen._backBuffer1.SHtransBlitFrom(frame, pt);
screen.slamArea(pt.x, pt.y, pt.x + frame._width, pt.y + frame._height);
}
@@ -442,7 +443,7 @@ void ScalpelUserInterface::restoreButton(int num) {
events.setCursor(ARROW);
// Restore the UI on the back buffer
- screen._backBuffer1.blitFrom(screen._backBuffer2, pt,
+ screen._backBuffer1.SHblitFrom(screen._backBuffer2, pt,
Common::Rect(pt.x, pt.y, pt.x + 90, pt.y + 19));
screen.slamArea(pt.x, pt.y, pt.x + frame.w, pt.y + frame.h);
@@ -489,7 +490,7 @@ void ScalpelUserInterface::toggleButton(uint16 num) {
ImageFrame &frame = (*_controls)[num];
Common::Point pt(MENU_POINTS[num][0], MENU_POINTS[num][1]);
offsetButton3DO(pt, num);
- screen._backBuffer1.transBlitFrom(frame, pt);
+ screen._backBuffer1.SHtransBlitFrom(frame, pt);
screen.slamArea(pt.x, pt.y, pt.x + frame._width, pt.y + frame._height);
}
} else {
@@ -501,7 +502,7 @@ void ScalpelUserInterface::toggleButton(uint16 num) {
void ScalpelUserInterface::clearInfo() {
if (_infoFlag) {
- _vm->_screen->vgaBar(Common::Rect(IS_3DO ? 33 : 16, INFO_LINE,
+ _vm->_screen->vgaBar(Common::Rect(IS_3DO ? 33 : 16, INFO_LINE,
SHERLOCK_SCREEN_WIDTH - (IS_3DO ? 33 : 19), INFO_LINE + 10), INFO_BLACK);
_infoFlag = false;
_oldLook = -1;
@@ -605,74 +606,92 @@ void ScalpelUserInterface::lookScreen(const Common::Point &pt) {
// If inventory is active and an item is selected for a Use or Give action
if ((_menuMode == INV_MODE || _menuMode == USE_MODE || _menuMode == GIVE_MODE) &&
(inv._invMode == INVMODE_USE || inv._invMode == INVMODE_GIVE)) {
- int width1 = 0, width2 = 0;
- int x, width;
+ int width1 = 0, width2 = 0, width3 = 0;
+ int x;
+
if (inv._invMode == INVMODE_USE) {
// Using an object
- x = width = screen.stringWidth("Use ");
+ Common::String useText1 = FIXED(UserInterface_Use);
+ Common::String useText2;
+ Common::String useText3;
- if (temp < 1000 && scene._bgShapes[temp]._aType != PERSON)
- // It's not a person, so make it lowercase
- tempStr.setChar(tolower(tempStr[0]), 0);
+ x = width1 = screen.stringWidth(useText1);
- x += screen.stringWidth(tempStr);
+ if (temp < 1000 && scene._bgShapes[temp]._aType != PERSON) {
+ // It's not a person, so make it lowercase
+ switch (_vm->getLanguage()) {
+ case Common::DE_DEU:
+ case Common::ES_ESP:
+ // don't do this for German + Spanish version
+ break;
+ default:
+ tempStr.setChar(tolower(tempStr[0]), 0);
+ break;
+ }
+ }
// If we're using an inventory object, add in the width
// of the object name and the " on "
if (_selector != -1) {
- width1 = screen.stringWidth(inv[_selector]._name);
- x += width1;
- width2 = screen.stringWidth(" on ");
+ useText2 = inv[_selector]._name;
+ width2 = screen.stringWidth(useText2);
x += width2;
+
+ useText3 = Common::String::format(FIXED(UserInterface_UseOn), tempStr.c_str());
+
+ } else {
+ useText3 = tempStr;
}
+ width3 = screen.stringWidth(useText3);
+ x += width3;
+
// If the line will be too long, keep cutting off characters
// until the string will fit
while (x > 280) {
- x -= screen.charWidth(tempStr.lastChar());
- tempStr.deleteLastChar();
+ x -= screen.charWidth(useText3.lastChar());
+ useText3.deleteLastChar();
}
int xStart = (SHERLOCK_SCREEN_WIDTH - x) / 2;
screen.print(Common::Point(xStart, INFO_LINE + 1),
- INFO_FOREGROUND, "Use ");
+ INFO_FOREGROUND, "%s", useText1.c_str());
if (_selector != -1) {
- screen.print(Common::Point(xStart + width, INFO_LINE + 1),
- TALK_FOREGROUND, "%s", inv[_selector]._name.c_str());
- screen.print(Common::Point(xStart + width + width1, INFO_LINE + 1),
- INFO_FOREGROUND, " on ");
- screen.print(Common::Point(xStart + width + width1 + width2, INFO_LINE + 1),
- INFO_FOREGROUND, "%s", tempStr.c_str());
+ screen.print(Common::Point(xStart + width1, INFO_LINE + 1),
+ TALK_FOREGROUND, "%s", useText2.c_str());
+ screen.print(Common::Point(xStart + width1 + width2, INFO_LINE + 1),
+ INFO_FOREGROUND, "%s", useText3.c_str());
} else {
- screen.print(Common::Point(xStart + width, INFO_LINE + 1),
- INFO_FOREGROUND, "%s", tempStr.c_str());
+ screen.print(Common::Point(xStart + width1, INFO_LINE + 1),
+ INFO_FOREGROUND, "%s", useText3.c_str());
}
} else if (temp >= 0 && temp < 1000 && _selector != -1 &&
scene._bgShapes[temp]._aType == PERSON) {
+ Common::String giveText1 = FIXED(UserInterface_Give);
+ Common::String giveText2 = inv[_selector]._name;
+ Common::String giveText3 = Common::String::format(FIXED(UserInterface_GiveTo), tempStr.c_str());
+
// Giving an object to a person
- width1 = screen.stringWidth(inv[_selector]._name);
- x = width = screen.stringWidth("Give ");
- x += width1;
- width2 = screen.stringWidth(" to ");
+ x = width1 = screen.stringWidth(giveText1);
+ width2 = screen.stringWidth(giveText2);
x += width2;
- x += screen.stringWidth(tempStr);
+ width3 = screen.stringWidth(giveText3);
+ x += width3;
// Ensure string will fit on-screen
while (x > 280) {
- x -= screen.charWidth(tempStr.lastChar());
- tempStr.deleteLastChar();
+ x -= screen.charWidth(giveText3.lastChar());
+ giveText3.deleteLastChar();
}
int xStart = (SHERLOCK_SCREEN_WIDTH - x) / 2;
screen.print(Common::Point(xStart, INFO_LINE + 1),
- INFO_FOREGROUND, "Give ");
- screen.print(Common::Point(xStart + width, INFO_LINE + 1),
- TALK_FOREGROUND, "%s", inv[_selector]._name.c_str());
- screen.print(Common::Point(xStart + width + width1, INFO_LINE + 1),
- INFO_FOREGROUND, " to ");
- screen.print(Common::Point(xStart + width + width1 + width2, INFO_LINE + 1),
- INFO_FOREGROUND, "%s", tempStr.c_str());
+ INFO_FOREGROUND, "%s", giveText1.c_str());
+ screen.print(Common::Point(xStart + width1, INFO_LINE + 1),
+ TALK_FOREGROUND, "%s", giveText2.c_str());
+ screen.print(Common::Point(xStart + width1 + width2, INFO_LINE + 1),
+ INFO_FOREGROUND, "%s", giveText3.c_str());
}
} else {
screen.print(Common::Point(0, INFO_LINE + 1), INFO_FOREGROUND, "%s", tempStr.c_str());
@@ -909,7 +928,7 @@ void ScalpelUserInterface::doEnvControl() {
} while (saves._savegameIndex < (MAX_SAVEGAME_SLOTS - ONSCREEN_FILES_COUNT) && moreKeys);
} else if ((found == 5 && events._released) || _key == saves._hotkeyQuit) {
clearWindow();
- screen.print(Common::Point(0, CONTROLS_Y + 20), INV_FOREGROUND, saves._fixedTextQuitGameQuestion.c_str());
+ screen.print(Common::Point(0, CONTROLS_Y + 20), INV_FOREGROUND, "%s", saves._fixedTextQuitGameQuestion.c_str());
screen.vgaBar(Common::Rect(0, CONTROLS_Y, SHERLOCK_SCREEN_WIDTH, CONTROLS_Y + 10), BORDER_COLOR);
screen.makeButton(Common::Rect(112, CONTROLS_Y, 160, CONTROLS_Y + 10), 136, saves._fixedTextQuitGameYes);
@@ -1254,7 +1273,7 @@ void ScalpelUserInterface::doLookControl() {
// Need to close the window and depress the Look button
Common::Point pt(MENU_POINTS[0][0], MENU_POINTS[0][1]);
offsetButton3DO(pt, 0);
- screen._backBuffer2.blitFrom((*_controls)[0], pt);
+ screen._backBuffer2.SHblitFrom((*_controls)[0], pt);
banishWindow(true);
_windowBounds.top = CONTROLS_Y1;
@@ -1278,14 +1297,14 @@ void ScalpelUserInterface::doLookControl() {
// Looking at an inventory object
// Backup the user interface
Surface tempSurface(SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT - CONTROLS_Y1);
- tempSurface.blitFrom(screen._backBuffer2, Common::Point(0, 0),
+ tempSurface.SHblitFrom(screen._backBuffer2, Common::Point(0, 0),
Common::Rect(0, CONTROLS_Y1, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT));
inv.drawInventory(INVENTORY_DONT_DISPLAY);
banishWindow(true);
// Restore the ui
- screen._backBuffer2.blitFrom(tempSurface, Common::Point(0, CONTROLS_Y1));
+ screen._backBuffer2.SHblitFrom(tempSurface, Common::Point(0, CONTROLS_Y1));
_windowBounds.top = CONTROLS_Y1;
_key = _oldKey = _hotkeyLook;
@@ -1869,7 +1888,7 @@ void ScalpelUserInterface::journalControl() {
// Reset the palette
screen.setPalette(screen._cMap);
- screen._backBuffer1.blitFrom(screen._backBuffer2);
+ screen._backBuffer1.SHblitFrom(screen._backBuffer2);
scene.updateBackground();
screen.slamArea(0, 0, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT);
}
@@ -1903,9 +1922,9 @@ void ScalpelUserInterface::printObjectDesc(const Common::String &str, bool first
Common::Point pt(MENU_POINTS[0][0], MENU_POINTS[0][1]);
offsetButton3DO(pt, 0);
- tempSurface.blitFrom(screen._backBuffer2, Common::Point(0, 0),
- Common::Rect(pt.x, pt.y, pt.x + tempSurface.w(), pt.y + tempSurface.h()));
- screen._backBuffer2.transBlitFrom((*_controls)[0], pt);
+ tempSurface.SHblitFrom(screen._backBuffer2, Common::Point(0, 0),
+ Common::Rect(pt.x, pt.y, pt.x + tempSurface.width(), pt.y + tempSurface.height()));
+ screen._backBuffer2.SHtransBlitFrom((*_controls)[0], pt);
banishWindow(1);
events.setCursor(MAGNIFY);
@@ -1915,7 +1934,7 @@ void ScalpelUserInterface::printObjectDesc(const Common::String &str, bool first
_menuMode = LOOK_MODE;
events.clearEvents();
- screen._backBuffer2.blitFrom(tempSurface, pt);
+ screen._backBuffer2.SHblitFrom(tempSurface, pt);
} else {
events.setCursor(ARROW);
banishWindow(true);
@@ -2053,9 +2072,9 @@ void ScalpelUserInterface::summonWindow(const Surface &bgSurface, bool slideUp)
if (slideUp) {
// Gradually slide up the display of the window
- for (int idx = 1; idx <= bgSurface.h(); idx += 2) {
- screen._backBuffer->blitFrom(bgSurface, Common::Point(0, SHERLOCK_SCREEN_HEIGHT - idx),
- Common::Rect(0, 0, bgSurface.w(), idx));
+ for (int idx = 1; idx <= bgSurface.height(); idx += 2) {
+ screen._backBuffer->SHblitFrom(bgSurface, Common::Point(0, SHERLOCK_SCREEN_HEIGHT - idx),
+ Common::Rect(0, 0, bgSurface.width(), idx));
screen.slamRect(Common::Rect(0, SHERLOCK_SCREEN_HEIGHT - idx,
SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT));
@@ -2063,21 +2082,21 @@ void ScalpelUserInterface::summonWindow(const Surface &bgSurface, bool slideUp)
}
} else {
// Gradually slide down the display of the window
- for (int idx = 1; idx <= bgSurface.h(); idx += 2) {
- screen._backBuffer->blitFrom(bgSurface,
- Common::Point(0, SHERLOCK_SCREEN_HEIGHT - bgSurface.h()),
- Common::Rect(0, bgSurface.h() - idx, bgSurface.w(), bgSurface.h()));
- screen.slamRect(Common::Rect(0, SHERLOCK_SCREEN_HEIGHT - bgSurface.h(),
- SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT - bgSurface.h() + idx));
+ for (int idx = 1; idx <= bgSurface.height(); idx += 2) {
+ screen._backBuffer->SHblitFrom(bgSurface,
+ Common::Point(0, SHERLOCK_SCREEN_HEIGHT - bgSurface.height()),
+ Common::Rect(0, bgSurface.height() - idx, bgSurface.width(), bgSurface.height()));
+ screen.slamRect(Common::Rect(0, SHERLOCK_SCREEN_HEIGHT - bgSurface.height(),
+ SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT - bgSurface.height() + idx));
events.delay(10);
}
}
// Final display of the entire window
- screen._backBuffer->blitFrom(bgSurface, Common::Point(0, SHERLOCK_SCREEN_HEIGHT - bgSurface.h()),
- Common::Rect(0, 0, bgSurface.w(), bgSurface.h()));
- screen.slamArea(0, SHERLOCK_SCREEN_HEIGHT - bgSurface.h(), bgSurface.w(), bgSurface.h());
+ screen._backBuffer->SHblitFrom(bgSurface, Common::Point(0, SHERLOCK_SCREEN_HEIGHT - bgSurface.height()),
+ Common::Rect(0, 0, bgSurface.width(), bgSurface.height()));
+ screen.slamArea(0, SHERLOCK_SCREEN_HEIGHT - bgSurface.height(), bgSurface.width(), bgSurface.height());
_windowOpen = true;
}
@@ -2088,10 +2107,10 @@ void ScalpelUserInterface::summonWindow(bool slideUp, int height) {
// Extract the window that's been drawn on the back buffer
Surface tempSurface(SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT - height);
Common::Rect r(0, height, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT);
- tempSurface.blitFrom(screen._backBuffer1, Common::Point(0, 0), r);
+ tempSurface.SHblitFrom(screen._backBuffer1, Common::Point(0, 0), r);
// Remove drawn window with original user interface
- screen._backBuffer1.blitFrom(screen._backBuffer2,
+ screen._backBuffer1.SHblitFrom(screen._backBuffer2,
Common::Point(0, height), r);
// Display the window gradually on-screen
@@ -2115,7 +2134,7 @@ void ScalpelUserInterface::banishWindow(bool slideUp) {
Common::copy_backward(pSrc, pSrcEnd, pDest);
// Restore lines from the ui in the secondary back buffer
- screen._backBuffer1.blitFrom(screen._backBuffer2,
+ screen._backBuffer1.SHblitFrom(screen._backBuffer2,
Common::Point(0, CONTROLS_Y),
Common::Rect(0, CONTROLS_Y, SHERLOCK_SCREEN_WIDTH, CONTROLS_Y + idx));
@@ -2125,14 +2144,14 @@ void ScalpelUserInterface::banishWindow(bool slideUp) {
}
// Restore final two old lines
- screen._backBuffer1.blitFrom(screen._backBuffer2,
+ screen._backBuffer1.SHblitFrom(screen._backBuffer2,
Common::Point(0, SHERLOCK_SCREEN_HEIGHT - 2),
Common::Rect(0, SHERLOCK_SCREEN_HEIGHT - 2,
SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT));
screen.slamArea(0, SHERLOCK_SCREEN_HEIGHT - 2, SHERLOCK_SCREEN_WIDTH, 2);
} else {
// Restore old area to completely erase window
- screen._backBuffer1.blitFrom(screen._backBuffer2,
+ screen._backBuffer1.SHblitFrom(screen._backBuffer2,
Common::Point(0, CONTROLS_Y),
Common::Rect(0, CONTROLS_Y, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT));
screen.slamRect(Common::Rect(0, CONTROLS_Y, SHERLOCK_SCREEN_WIDTH,
@@ -2152,7 +2171,7 @@ void ScalpelUserInterface::banishWindow(bool slideUp) {
}
// Show entire final area
- screen._backBuffer1.blitFrom(screen._backBuffer2, Common::Point(0, CONTROLS_Y1),
+ screen._backBuffer1.SHblitFrom(screen._backBuffer2, Common::Point(0, CONTROLS_Y1),
Common::Rect(0, CONTROLS_Y1, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT));
screen.slamRect(Common::Rect(0, CONTROLS_Y1, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT));
}
@@ -2234,7 +2253,7 @@ void ScalpelUserInterface::checkUseAction(const UseType *use, const Common::Stri
if (scene._goToScene != 1 && !printed && !talk._talkToAbort) {
_infoFlag = true;
clearInfo();
- screen.print(Common::Point(0, INFO_LINE + 1), INFO_FOREGROUND, "Done...");
+ screen.print(Common::Point(0, INFO_LINE + 1), INFO_FOREGROUND, "%s", FIXED(UserInterface_Done));
_menuCounter = 25;
}
}
@@ -2244,9 +2263,9 @@ void ScalpelUserInterface::checkUseAction(const UseType *use, const Common::Stri
clearInfo();
if (giveMode) {
- screen.print(Common::Point(0, INFO_LINE + 1), INFO_FOREGROUND, "No, thank you.");
+ screen.print(Common::Point(0, INFO_LINE + 1), INFO_FOREGROUND, "%s", FIXED(UserInterface_NoThankYou));
} else if (fixedTextActionId == kFixedTextAction_Invalid) {
- screen.print(Common::Point(0, INFO_LINE + 1), INFO_FOREGROUND, "You can't do that.");
+ screen.print(Common::Point(0, INFO_LINE + 1), INFO_FOREGROUND, "%s", FIXED(UserInterface_YouCantDoThat));
} else {
Common::String errorMessage = fixedText.getActionMessage(fixedTextActionId, 0);
screen.print(Common::Point(0, INFO_LINE + 1), INFO_FOREGROUND, "%s", errorMessage.c_str());
diff --git a/engines/sherlock/scalpel/tsage/logo.cpp b/engines/sherlock/scalpel/tsage/logo.cpp
index 273d26df74..a885057f35 100644
--- a/engines/sherlock/scalpel/tsage/logo.cpp
+++ b/engines/sherlock/scalpel/tsage/logo.cpp
@@ -217,7 +217,7 @@ void Object::erase() {
Screen &screen = *_vm->_screen;
if (_visage.isLoaded() && !_oldBounds.isEmpty())
- screen.blitFrom(screen._backBuffer1, Common::Point(_oldBounds.left, _oldBounds.top), _oldBounds);
+ screen.SHblitFrom(screen._backBuffer1, Common::Point(_oldBounds.left, _oldBounds.top), _oldBounds);
}
void Object::update() {
@@ -246,9 +246,9 @@ void Object::update() {
_visage.getFrame(s, _frame);
// Display the frame
- _oldBounds = Common::Rect(_position.x, _position.y, _position.x + s.w(), _position.y + s.h());
+ _oldBounds = Common::Rect(_position.x, _position.y, _position.x + s.width(), _position.y + s.height());
_oldBounds.translate(-s._centroid.x, -s._centroid.y);
- screen.transBlitFrom(s, Common::Point(_oldBounds.left, _oldBounds.top));
+ screen.SHtransBlitFrom(s, Common::Point(_oldBounds.left, _oldBounds.top));
}
}
@@ -652,7 +652,7 @@ void Logo::loadBackground() {
screen.setPalette(palette);
// Copy the surface to the screen
- screen.blitFrom(screen._backBuffer1);
+ screen.SHblitFrom(screen._backBuffer1);
}
void Logo::fade(const byte palette[PALETTE_SIZE], int step) {
diff --git a/engines/sherlock/scene.cpp b/engines/sherlock/scene.cpp
index 6f9ef179a3..78d0cd862c 100644
--- a/engines/sherlock/scene.cpp
+++ b/engines/sherlock/scene.cpp
@@ -27,6 +27,7 @@
#include "sherlock/scalpel/scalpel_people.h"
#include "sherlock/scalpel/scalpel_scene.h"
#include "sherlock/scalpel/scalpel_screen.h"
+#include "sherlock/scalpel/3do/scalpel_3do_screen.h"
#include "sherlock/tattoo/tattoo.h"
#include "sherlock/tattoo/tattoo_scene.h"
#include "sherlock/tattoo/tattoo_user_interface.h"
@@ -356,7 +357,7 @@ bool Scene::loadScene(const Common::String &filename) {
if (IS_ROSE_TATTOO) {
// Resize the screen if necessary
int fullWidth = SHERLOCK_SCREEN_WIDTH + bgHeader._scrollSize;
- if (screen._backBuffer1.w() != fullWidth) {
+ if (screen._backBuffer1.width() != fullWidth) {
screen._backBuffer1.create(fullWidth, SHERLOCK_SCREEN_HEIGHT);
screen._backBuffer2.create(fullWidth, SHERLOCK_SCREEN_HEIGHT);
}
@@ -649,7 +650,7 @@ bool Scene::loadScene(const Common::String &filename) {
}
// Backup the image and set the palette
- screen._backBuffer2.blitFrom(screen._backBuffer1);
+ screen._backBuffer2.SHblitFrom(screen._backBuffer1);
screen.setPalette(screen._cMap);
delete rrmStream;
@@ -996,12 +997,12 @@ bool Scene::loadScene(const Common::String &filename) {
#if 0
// code to show the background
- screen.blitFrom(screen._backBuffer1);
+ screen.SHblitFrom(screen._backBuffer1);
_vm->_events->wait(10000);
#endif
// Backup the image
- screen._backBuffer2.blitFrom(screen._backBuffer1);
+ screen._backBuffer2.SHblitFrom(screen._backBuffer1);
}
// Handle drawing any on-screen interface
@@ -1236,7 +1237,7 @@ void Scene::transitionToScene() {
// If the scene is capable of scrolling, set the current scroll so that whoever has control
// of the scroll code is in the middle of the screen
- if (screen._backBuffer1.w() > SHERLOCK_SCREEN_WIDTH)
+ if (screen._backBuffer1.width() > SHERLOCK_SCREEN_WIDTH)
people[people._walkControl].centerScreenOnPerson();
for (uint objIdx = 0; objIdx < _bgShapes.size(); ++objIdx) {
diff --git a/engines/sherlock/screen.cpp b/engines/sherlock/screen.cpp
index 74da2a80ea..85d47c84dc 100644
--- a/engines/sherlock/screen.cpp
+++ b/engines/sherlock/screen.cpp
@@ -23,6 +23,8 @@
#include "sherlock/screen.h"
#include "sherlock/sherlock.h"
#include "sherlock/scalpel/scalpel_screen.h"
+#include "sherlock/scalpel/3do/scalpel_3do_screen.h"
+#include "sherlock/tattoo/tattoo_screen.h"
#include "common/system.h"
#include "common/util.h"
#include "graphics/palette.h"
@@ -31,17 +33,14 @@ namespace Sherlock {
Screen *Screen::init(SherlockEngine *vm) {
if (vm->getGameID() == GType_RoseTattoo)
- return new Screen(vm);
+ return new Tattoo::TattooScreen(vm);
else if (vm->getPlatform() == Common::kPlatform3DO)
return new Scalpel::Scalpel3DOScreen(vm);
else
return new Scalpel::ScalpelScreen(vm);
}
-Screen::Screen(SherlockEngine *vm) : Surface(g_system->getWidth(), g_system->getHeight()), _vm(vm),
- _backBuffer1(vm->getGameID() == GType_RoseTattoo ? 640 : 320, vm->getGameID() == GType_RoseTattoo ? 480 : 200),
- _backBuffer2(vm->getGameID() == GType_RoseTattoo ? 640 : 320, vm->getGameID() == GType_RoseTattoo ? 480 : 200),
- _backBuffer(&_backBuffer1) {
+Screen::Screen(SherlockEngine *vm) : Graphics::Screen(), _vm(vm), _backBuffer(&_backBuffer1) {
_transitionSeed = 1;
_fadeStyle = false;
Common::fill(&_cMap[0], &_cMap[PALETTE_SIZE], 0);
@@ -58,37 +57,7 @@ Screen::Screen(SherlockEngine *vm) : Surface(g_system->getWidth(), g_system->get
}
Screen::~Screen() {
- Fonts::free();
-}
-
-void Screen::update() {
- // Merge the dirty rects
- mergeDirtyRects();
-
- // Loop through copying dirty areas to the physical screen
- Common::List<Common::Rect>::iterator i;
- for (i = _dirtyRects.begin(); i != _dirtyRects.end(); ++i) {
- const Common::Rect &r = *i;
- const byte *srcP = (const byte *)getBasePtr(r.left, r.top);
- g_system->copyRectToScreen(srcP, _surface.pitch, r.left, r.top,
- r.width(), r.height());
- }
-
- // Signal the physical screen to update
- g_system->updateScreen();
- _dirtyRects.clear();
-}
-
-void Screen::makeAllDirty() {
- addDirtyRect(Common::Rect(0, 0, this->w(), this->h()));
-}
-
-void Screen::getPalette(byte palette[PALETTE_SIZE]) {
- g_system->getPaletteManager()->grabPalette(palette, 0, PALETTE_COUNT);
-}
-
-void Screen::setPalette(const byte palette[PALETTE_SIZE]) {
- g_system->getPaletteManager()->setPalette(palette, 0, PALETTE_COUNT);
+ Fonts::freeFont();
}
int Screen::equalizePalette(const byte palette[PALETTE_SIZE]) {
@@ -124,7 +93,7 @@ void Screen::fadeToBlack(int speed) {
}
setPalette(tempPalette);
- fillRect(Common::Rect(0, 0, _surface.w, _surface.h), 0);
+ fillRect(Common::Rect(0, 0, this->w, this->h), 0);
}
void Screen::fadeIn(const byte palette[PALETTE_SIZE], int speed) {
@@ -136,59 +105,23 @@ void Screen::fadeIn(const byte palette[PALETTE_SIZE], int speed) {
setPalette(palette);
}
-void Screen::addDirtyRect(const Common::Rect &r) {
- _dirtyRects.push_back(r);
- assert(r.width() > 0 && r.height() > 0);
-}
-
-void Screen::mergeDirtyRects() {
- Common::List<Common::Rect>::iterator rOuter, rInner;
-
- // Process the dirty rect list to find any rects to merge
- 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
-
- unionRectangle(*rOuter, *rOuter, *rInner);
-
- // remove the inner rect from the list
- _dirtyRects.erase(rInner);
-
- // move back to beginning of list
- rInner = rOuter;
- }
- }
- }
-}
-
-bool Screen::unionRectangle(Common::Rect &destRect, const Common::Rect &src1, const Common::Rect &src2) {
- destRect = src1;
- destRect.extend(src2);
-
- return !destRect.isEmpty();
-}
-
void Screen::randomTransition() {
Events &events = *_vm->_events;
const int TRANSITION_MULTIPLIER = 0x15a4e35;
- _dirtyRects.clear();
+ clearDirtyRects();
assert(IS_SERRATED_SCALPEL);
for (int idx = 0; idx <= 65535 && !_vm->shouldQuit(); ++idx) {
_transitionSeed = _transitionSeed * TRANSITION_MULTIPLIER + 1;
int offset = _transitionSeed & 0xFFFF;
- if (offset < (this->w() * this->h()))
+ if (offset < (this->width() * this->height()))
*((byte *)getPixels() + offset) = *((const byte *)_backBuffer->getPixels() + offset);
if (idx != 0 && (idx % 300) == 0) {
// Ensure there's a full screen dirty rect for the next frame update
- if (_dirtyRects.empty())
- addDirtyRect(Common::Rect(0, 0, _surface.w, _surface.h));
+ if (!isDirty())
+ addDirtyRect(Common::Rect(0, 0, this->w, this->h));
events.pollEvents();
events.delay(1);
@@ -196,7 +129,7 @@ void Screen::randomTransition() {
}
// Make sure everything has been transferred
- blitFrom(*_backBuffer);
+ SHblitFrom(*_backBuffer);
}
void Screen::verticalTransition() {
@@ -205,13 +138,13 @@ void Screen::verticalTransition() {
byte table[640];
Common::fill(&table[0], &table[640], 0);
- for (int yp = 0; yp < this->h(); ++yp) {
- for (int xp = 0; xp < this->w(); ++xp) {
- int temp = (table[xp] >= (this->h() - 3)) ? this->h() - table[xp] :
+ for (int yp = 0; yp < this->height(); ++yp) {
+ for (int xp = 0; xp < this->width(); ++xp) {
+ int temp = (table[xp] >= (this->height() - 3)) ? this->height() - table[xp] :
_vm->getRandomNumber(3) + 1;
if (temp) {
- blitFrom(_backBuffer1, Common::Point(xp, table[xp]),
+ SHblitFrom(_backBuffer1, Common::Point(xp, table[xp]),
Common::Rect(xp, table[xp], xp + 1, table[xp] + temp));
table[xp] += temp;
}
@@ -223,7 +156,7 @@ void Screen::verticalTransition() {
void Screen::restoreBackground(const Common::Rect &r) {
if (r.width() > 0 && r.height() > 0)
- _backBuffer->blitFrom(_backBuffer2, Common::Point(r.left, r.top), r);
+ _backBuffer->SHblitFrom(_backBuffer2, Common::Point(r.left, r.top), r);
}
void Screen::slamArea(int16 xp, int16 yp, int16 width, int16 height) {
@@ -254,11 +187,10 @@ void Screen::slamRect(const Common::Rect &r) {
}
if (srcRect.isValidRect())
- blitFrom(*_backBuffer, Common::Point(destRect.left, destRect.top), srcRect);
+ SHblitFrom(*_backBuffer, Common::Point(destRect.left, destRect.top), srcRect);
}
}
-
void Screen::flushImage(ImageFrame *frame, const Common::Point &pt, int16 *xp, int16 *yp,
int16 *width, int16 *height) {
Common::Point imgPos = pt + frame->_offset;
@@ -335,7 +267,7 @@ void Screen::blockMove(const Common::Rect &r) {
}
void Screen::blockMove() {
- blockMove(Common::Rect(0, 0, w(), h()));
+ blockMove(Common::Rect(0, 0, width(), height()));
}
void Screen::print(const Common::Point &pt, uint color, const char *formatStr, ...) {
@@ -351,13 +283,13 @@ void Screen::print(const Common::Point &pt, uint color, const char *formatStr, .
pos.y--; // Font is always drawing one line higher
if (!pos.x)
// Center text horizontally
- pos.x = (this->w() - width) / 2;
+ pos.x = (this->width() - width) / 2;
Common::Rect textBounds(pos.x, pos.y, pos.x + width, pos.y + _fontHeight);
- if (textBounds.right > this->w())
- textBounds.moveTo(this->w() - width, textBounds.top);
- if (textBounds.bottom > this->h())
- textBounds.moveTo(textBounds.left, this->h() - _fontHeight);
+ if (textBounds.right > this->width())
+ textBounds.moveTo(this->width() - width, textBounds.top);
+ if (textBounds.bottom > this->height())
+ textBounds.moveTo(textBounds.left, this->height() - _fontHeight);
// Write out the string at the given position
writeString(str, Common::Point(textBounds.left, textBounds.top), color);
@@ -387,7 +319,8 @@ void Screen::vgaBar(const Common::Rect &r, int color) {
}
void Screen::setDisplayBounds(const Common::Rect &r) {
- _sceneSurface.setPixels(_backBuffer1.getBasePtr(r.left, r.top), r.width(), r.height(), _backBuffer1.getPixelFormat());
+ _sceneSurface.setPixels((byte *)_backBuffer1.getBasePtr(r.left, r.top),
+ r.width(), r.height(), _backBuffer1.format);
_backBuffer = &_sceneSurface;
}
@@ -397,8 +330,8 @@ void Screen::resetDisplayBounds() {
}
Common::Rect Screen::getDisplayBounds() {
- return (_backBuffer == &_sceneSurface) ? Common::Rect(0, 0, _sceneSurface.w(), _sceneSurface.h()) :
- Common::Rect(0, 0, this->w(), this->h());
+ return (_backBuffer == &_sceneSurface) ? Common::Rect(0, 0, _sceneSurface.width(), _sceneSurface.height()) :
+ Common::Rect(0, 0, this->width(), this->height());
}
void Screen::synchronize(Serializer &s) {
diff --git a/engines/sherlock/screen.h b/engines/sherlock/screen.h
index 04a0c1e505..ceeb1297a3 100644
--- a/engines/sherlock/screen.h
+++ b/engines/sherlock/screen.h
@@ -25,52 +25,30 @@
#include "common/list.h"
#include "common/rect.h"
+#include "graphics/screen.h"
+#include "sherlock/image_file.h"
#include "sherlock/surface.h"
#include "sherlock/resources.h"
#include "sherlock/saveload.h"
namespace Sherlock {
-#define PALETTE_SIZE 768
-#define PALETTE_COUNT 256
#define VGA_COLOR_TRANS(x) ((x) * 255 / 63)
#define BG_GREYSCALE_RANGE_END 229
#define BLACK 0
class SherlockEngine;
-class Screen : public Surface {
+class Screen : virtual public Graphics::Screen, virtual public Surface {
private:
- Common::List<Common::Rect> _dirtyRects;
uint32 _transitionSeed;
Surface _sceneSurface;
// Rose Tattoo fields
int _fadeBytesRead, _fadeBytesToRead;
int _oldFadePercent;
-private:
- /**
- * Merges together overlapping dirty areas of the screen
- */
- void mergeDirtyRects();
-
- /**
- * Returns the union of two dirty area rectangles
- */
- bool unionRectangle(Common::Rect &destRect, const Common::Rect &src1, const Common::Rect &src2);
protected:
SherlockEngine *_vm;
-
- /**
- * Clear the current dirty rects list
- */
- void clearDirtyRects() { _dirtyRects.clear(); }
-
- /**
- * Adds a rectangle to the list of modified areas of the screen during the
- * current frame
- */
- virtual void addDirtyRect(const Common::Rect &r);
public:
Surface _backBuffer1, _backBuffer2;
Surface *_backBuffer;
@@ -86,26 +64,6 @@ public:
virtual ~Screen();
/**
- * Handles updating any dirty areas of the screen Surface object to the physical screen
- */
- void update();
-
- /**
- * Makes the whole screen dirty
- */
- void makeAllDirty();
-
- /**
- * Return the currently active palette
- */
- void getPalette(byte palette[PALETTE_SIZE]);
-
- /**
- * Set the palette
- */
- void setPalette(const byte palette[PALETTE_SIZE]);
-
- /**
* Fades from the currently active palette to the passed palette
*/
int equalizePalette(const byte palette[PALETTE_SIZE]);
diff --git a/engines/sherlock/sherlock.h b/engines/sherlock/sherlock.h
index b85321c385..d3b2d0cac8 100644
--- a/engines/sherlock/sherlock.h
+++ b/engines/sherlock/sherlock.h
@@ -63,9 +63,9 @@ enum GameType {
GType_RoseTattoo = 1
};
-#define SHERLOCK_SCREEN_WIDTH _vm->_screen->w()
-#define SHERLOCK_SCREEN_HEIGHT _vm->_screen->h()
-#define SHERLOCK_SCENE_WIDTH _vm->_screen->_backBuffer1.w()
+#define SHERLOCK_SCREEN_WIDTH _vm->_screen->width()
+#define SHERLOCK_SCREEN_HEIGHT _vm->_screen->height()
+#define SHERLOCK_SCENE_WIDTH _vm->_screen->_backBuffer1.width()
#define SHERLOCK_SCENE_HEIGHT (IS_SERRATED_SCALPEL ? 138 : 480)
#define SCENES_COUNT (IS_SERRATED_SCALPEL ? 63 : 101)
#define MAX_BGSHAPES (IS_SERRATED_SCALPEL ? 64 : 150)
diff --git a/engines/sherlock/sound.cpp b/engines/sherlock/sound.cpp
index 5a9f0c2ec6..e5b1099123 100644
--- a/engines/sherlock/sound.cpp
+++ b/engines/sherlock/sound.cpp
@@ -120,7 +120,9 @@ byte Sound::decodeSample(byte sample, byte &reference, int16 &scale) {
}
bool Sound::playSound(const Common::String &name, WaitType waitType, int priority, const char *libraryFilename) {
- stopSound();
+ // Scalpel has only a single sound handle, so it must be stopped before starting a new sound
+ if (IS_SERRATED_SCALPEL)
+ stopSound();
Common::String filename = formFilename(name);
diff --git a/engines/sherlock/surface.cpp b/engines/sherlock/surface.cpp
index b7fc76325c..46304e1b2f 100644
--- a/engines/sherlock/surface.cpp
+++ b/engines/sherlock/surface.cpp
@@ -21,245 +21,24 @@
*/
#include "sherlock/surface.h"
-#include "sherlock/sherlock.h"
-#include "sherlock/resources.h"
-#include "common/system.h"
-#include "graphics/palette.h"
+#include "sherlock/fonts.h"
namespace Sherlock {
-Surface::Surface(uint16 width, uint16 height) : Fonts(), _freePixels(true) {
- create(width, height);
-}
-
-Surface::Surface() : Fonts(), _freePixels(false) {
-}
-
-Surface::~Surface() {
- if (_freePixels)
- _surface.free();
-}
-
-void Surface::create(uint16 width, uint16 height) {
- if (_freePixels)
- _surface.free();
-
- if (_vm->getPlatform() == Common::kPlatform3DO) {
- _surface.create(width, height, Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0));
- } else {
- _surface.create(width, height, Graphics::PixelFormat::createFormatCLUT8());
- }
- _freePixels = true;
-}
-
-Graphics::PixelFormat Surface::getPixelFormat() {
- return _surface.format;
-}
-
-void Surface::blitFrom(const Surface &src) {
- blitFrom(src, Common::Point(0, 0));
-}
-
-void Surface::blitFrom(const ImageFrame &src) {
- blitFrom(src._frame, Common::Point(0, 0));
-}
-
-void Surface::blitFrom(const Graphics::Surface &src) {
- blitFrom(src, Common::Point(0, 0));
-}
-
-void Surface::blitFrom(const Surface &src, const Common::Point &pt) {
- blitFrom(src, pt, Common::Rect(0, 0, src._surface.w, src._surface.h));
-}
-
-void Surface::blitFrom(const ImageFrame &src, const Common::Point &pt) {
- blitFrom(src._frame, pt, Common::Rect(0, 0, src._frame.w, src._frame.h));
-}
-
-void Surface::blitFrom(const Graphics::Surface &src, const Common::Point &pt) {
- blitFrom(src, pt, Common::Rect(0, 0, src.w, src.h));
-}
-
-void Surface::blitFrom(const Graphics::Surface &src, const Common::Point &pt, const Common::Rect &srcBounds) {
- Common::Rect srcRect = srcBounds;
- Common::Rect destRect(pt.x, pt.y, pt.x + srcRect.width(), pt.y + srcRect.height());
-
- if (srcRect.isValidRect() && clip(srcRect, destRect)) {
- // Surface is at least partially or completely on-screen
- addDirtyRect(destRect);
- _surface.copyRectToSurface(src, destRect.left, destRect.top, srcRect);
- }
-}
-
-void Surface::blitFrom(const ImageFrame &src, const Common::Point &pt, const Common::Rect &srcBounds) {
- blitFrom(src._frame, pt, srcBounds);
-}
-
-void Surface::blitFrom(const Surface &src, const Common::Point &pt, const Common::Rect &srcBounds) {
- blitFrom(src._surface, pt, srcBounds);
-}
-
-void Surface::transBlitFrom(const ImageFrame &src, const Common::Point &pt,
- bool flipped, int overrideColor, int scaleVal) {
- Common::Point drawPt(pt.x + src.sDrawXOffset(scaleVal), pt.y + src.sDrawYOffset(scaleVal));
- transBlitFrom(src._frame, drawPt, flipped, overrideColor, scaleVal);
-}
-
-void Surface::transBlitFrom(const Surface &src, const Common::Point &pt,
- bool flipped, int overrideColor, int scaleVal) {
- const Graphics::Surface &s = src._surface;
- transBlitFrom(s, pt, flipped, overrideColor, scaleVal);
-}
-
-void Surface::transBlitFrom(const Graphics::Surface &src, const Common::Point &pt,
- bool flipped, int overrideColor, int scaleVal) {
- if (scaleVal == SCALE_THRESHOLD) {
- transBlitFromUnscaled(src, pt, flipped, overrideColor);
- return;
- }
-
- int destWidth = src.w * SCALE_THRESHOLD / scaleVal;
- int destHeight = src.h * SCALE_THRESHOLD / scaleVal;
-
- // Loop through drawing output lines
- for (int destY = pt.y, scaleYCtr = 0; destY < (pt.y + destHeight); ++destY, scaleYCtr += scaleVal) {
- if (destY < 0 || destY >= this->h())
- continue;
- const byte *srcLine = (const byte *)src.getBasePtr(0, scaleYCtr / SCALE_THRESHOLD);
- byte *destLine = (byte *)getBasePtr(pt.x, destY);
-
- // Loop through drawing individual rows
- for (int xCtr = 0, scaleXCtr = 0; xCtr < destWidth; ++xCtr, scaleXCtr += scaleVal) {
- int destX = pt.x + xCtr;
- if (destX < 0 || destX >= this->w())
- continue;
-
- byte srcVal = srcLine[flipped ? src.w - scaleXCtr / SCALE_THRESHOLD - 1 : scaleXCtr / SCALE_THRESHOLD];
- if (srcVal != TRANSPARENCY)
- destLine[xCtr] = srcVal;
- }
- }
-
- // Mark the affected area
- addDirtyRect(Common::Rect(pt.x, pt.y, pt.x + destWidth, pt.y + destHeight));
-}
-
-void Surface::transBlitFromUnscaled(const Graphics::Surface &src, const Common::Point &pt,
- bool flipped, int overrideColor) {
- Common::Rect drawRect(0, 0, src.w, src.h);
- Common::Rect destRect(pt.x, pt.y, pt.x + src.w, pt.y + src.h);
-
- // Clip the display area to on-screen
- if (!clip(drawRect, destRect))
- // It's completely off-screen
- return;
-
- if (flipped)
- drawRect = Common::Rect(src.w - drawRect.right, drawRect.top,
- src.w - drawRect.left, drawRect.bottom);
-
- Common::Point destPt(destRect.left, destRect.top);
- addDirtyRect(Common::Rect(destPt.x, destPt.y, destPt.x + drawRect.width(),
- destPt.y + drawRect.height()));
-
- switch (src.format.bytesPerPixel) {
- case 1:
- // 8-bit palettized: Draw loop
- assert(_surface.format.bytesPerPixel == 1); // Security check
- for (int yp = 0; yp < drawRect.height(); ++yp) {
- const byte *srcP = (const byte *)src.getBasePtr(
- flipped ? drawRect.right - 1 : drawRect.left, drawRect.top + yp);
- byte *destP = (byte *)getBasePtr(destPt.x, destPt.y + yp);
-
- for (int xp = 0; xp < drawRect.width(); ++xp, ++destP) {
- if (*srcP != TRANSPARENCY)
- *destP = overrideColor ? overrideColor : *srcP;
-
- srcP = flipped ? srcP - 1 : srcP + 1;
- }
- }
- break;
- case 2:
- // 3DO 15-bit RGB565: Draw loop
- assert(_surface.format.bytesPerPixel == 2); // Security check
- for (int yp = 0; yp < drawRect.height(); ++yp) {
- const uint16 *srcP = (const uint16 *)src.getBasePtr(
- flipped ? drawRect.right - 1 : drawRect.left, drawRect.top + yp);
- uint16 *destP = (uint16 *)getBasePtr(destPt.x, destPt.y + yp);
-
- for (int xp = 0; xp < drawRect.width(); ++xp, ++destP) {
- if (*srcP) // RGB 0, 0, 0 -> transparent on 3DO
- *destP = *srcP; // overrideColor ? overrideColor : *srcP;
-
- srcP = flipped ? srcP - 1 : srcP + 1;
- }
- }
- break;
- default:
- error("Surface: unsupported bytesperpixel");
- break;
- }
+Surface::Surface() : Graphics::ManagedSurface(), Fonts() {
}
-void Surface::fillRect(int x1, int y1, int x2, int y2, uint color) {
- fillRect(Common::Rect(x1, y1, x2, y2), color);
-}
-
-void Surface::fillRect(const Common::Rect &r, uint color) {
- _surface.fillRect(r, color);
- addDirtyRect(r);
-}
-
-void Surface::fill(uint color) {
- fillRect(Common::Rect(_surface.w, _surface.h), color);
-}
-
-bool Surface::clip(Common::Rect &srcBounds, Common::Rect &destBounds) {
- if (destBounds.left >= w() || destBounds.top >= h() ||
- destBounds.right <= 0 || destBounds.bottom <= 0)
- return false;
-
- // Clip the bounds if necessary to fit on-screen
- if (destBounds.right > w()) {
- srcBounds.right -= destBounds.right - w();
- destBounds.right = w();
- }
-
- if (destBounds.bottom > h()) {
- srcBounds.bottom -= destBounds.bottom - h();
- destBounds.bottom = h();
- }
-
- if (destBounds.top < 0) {
- srcBounds.top += -destBounds.top;
- destBounds.top = 0;
- }
-
- if (destBounds.left < 0) {
- srcBounds.left += -destBounds.left;
- destBounds.left = 0;
- }
-
- return true;
-}
-
-void Surface::clear() {
- fillRect(Common::Rect(0, 0, w(), h()), 0);
-}
-
-void Surface::free() {
- if (_freePixels) {
- _surface.free();
- _freePixels = false;
- }
+Surface::Surface(int width, int height) : Graphics::ManagedSurface(width, height),
+ Fonts() {
+ create(width, height);
}
-void Surface::setPixels(byte *pixels, int width, int height, Graphics::PixelFormat pixelFormat) {
- _surface.format = pixelFormat;
- _surface.w = width;
- _surface.h = height;
- _surface.pitch = width * pixelFormat.bytesPerPixel;
- _surface.setPixels(pixels);
+void Surface::setPixels(byte *pixelsPtr, int sizeX, int sizeY, const Graphics::PixelFormat &pixFormat) {
+ Graphics::ManagedSurface::setPixels(pixelsPtr);
+ this->format = pixFormat;
+ this->w = sizeX;
+ this->h = sizeY;
+ this->pitch = sizeX * pixFormat.bytesPerPixel;
}
void Surface::writeString(const Common::String &str, const Common::Point &pt, uint overrideColor) {
@@ -278,4 +57,21 @@ void Surface::writeFancyString(const Common::String &str, const Common::Point &p
writeString(str, Common::Point(pt.x + 1, pt.y + 1), overrideColor2);
}
+void Surface::SHtransBlitFrom(const ImageFrame &src, const Common::Point &pt,
+ bool flipped, int overrideColor, int scaleVal) {
+ Common::Point drawPt(pt.x + src.sDrawXOffset(scaleVal), pt.y + src.sDrawYOffset(scaleVal));
+ SHtransBlitFrom(src._frame, drawPt, flipped, overrideColor, scaleVal);
+}
+
+void Surface::SHtransBlitFrom(const Graphics::Surface &src, const Common::Point &pt,
+ bool flipped, int overrideColor, int scaleVal) {
+ Common::Rect srcRect(0, 0, src.w, src.h);
+ Common::Rect destRect(pt.x, pt.y, pt.x + src.w * SCALE_THRESHOLD / scaleVal,
+ pt.y + src.h * SCALE_THRESHOLD / scaleVal);
+
+ Graphics::ManagedSurface::transBlitFrom(src, srcRect, destRect, TRANSPARENCY,
+ flipped, overrideColor);
+}
+
+
} // End of namespace Sherlock
diff --git a/engines/sherlock/surface.h b/engines/sherlock/surface.h
index 378c9be9cd..648b121852 100644
--- a/engines/sherlock/surface.h
+++ b/engines/sherlock/surface.h
@@ -20,165 +20,101 @@
*
*/
-#ifndef SHERLOCK_GRAPHICS_H
-#define SHERLOCK_GRAPHICS_H
+#ifndef SHERLOCK_SURFACE_H
+#define SHERLOCK_SURFACE_H
#include "common/rect.h"
#include "common/platform.h"
-#include "graphics/surface.h"
+#include "graphics/managed_surface.h"
#include "sherlock/fonts.h"
+#include "sherlock/image_file.h"
namespace Sherlock {
#define SCALE_THRESHOLD 0x100
#define TRANSPARENCY 255
-struct ImageFrame;
-
-class Surface: public Fonts {
-private:
- bool _freePixels;
-
- /**
- * Copy a surface into this one
- */
- void blitFrom(const Graphics::Surface &src);
-protected:
- Graphics::Surface _surface;
-
- /**
- * Clips the given source bounds so the passed destBounds will be entirely on-screen
- */
- bool clip(Common::Rect &srcBounds, Common::Rect &destBounds);
-
- /**
- * Base method stub for signalling dirty rect areas
- */
- virtual void addDirtyRect(const Common::Rect &r) {}
-
- /**
- * Draws a sub-section of a surface at a given position within this surface
- */
- virtual void blitFrom(const Graphics::Surface &src, const Common::Point &pt, const Common::Rect &srcBounds);
-
+/**
+ * Implements a descendent surface that combines both a managed surface and the font
+ * drawing code. It also introduces a series of drawing method stubs that the 3DO
+ * Serrated Scalpel screen overrides to implement sprite doubling
+ */
+class Surface: virtual public Graphics::ManagedSurface, public Fonts {
+public:
/**
- * Draws a surface at a given position within this surface with transparency
+ * Constructor
*/
- virtual void transBlitFromUnscaled(const Graphics::Surface &src, const Common::Point &pt, bool flipped,
- int overrideColor);
-public:
- Surface(uint16 width, uint16 height);
Surface();
- virtual ~Surface();
-
+
/**
- * Sets up an internal surface with the specified dimensions that will be automatically freed
- * when the surface object is destroyed
+ * Constructor
*/
- void create(uint16 width, uint16 height);
-
- Graphics::PixelFormat getPixelFormat();
+ Surface(int width, int height);
/**
- * Copy a surface into this one
+ * Set the surface details
*/
- void blitFrom(const Surface &src);
+ void setPixels(byte *pixelsPtr, int sizeX, int sizeY, const Graphics::PixelFormat &pixFormat);
/**
- * Copy an image frame into this surface
+ * Draws a surface on this surface
*/
- void blitFrom(const ImageFrame &src);
+ virtual void SHblitFrom(const Graphics::Surface &src) {
+ Graphics::ManagedSurface::blitFrom(src);
+ }
/**
* Draws a surface at a given position within this surface
*/
- void blitFrom(const Surface &src, const Common::Point &pt);
-
- /**
- * Copy an image frame onto this surface at a given position
- */
- void blitFrom(const ImageFrame &src, const Common::Point &pt);
+ virtual void SHblitFrom(const Graphics::Surface &src, const Common::Point &destPos) {
+ Graphics::ManagedSurface::blitFrom(src, destPos);
+ }
/**
* Draws a sub-section of a surface at a given position within this surface
*/
- void blitFrom(const Surface &src, const Common::Point &pt, const Common::Rect &srcBounds);
-
- /**
- * Copy a sub-area of a source image frame into this surface at a given position
- */
- void blitFrom(const ImageFrame &src, const Common::Point &pt, const Common::Rect &srcBounds);
-
- /**
- * Draws a surface at a given position within this surface
- */
- void blitFrom(const Graphics::Surface &src, const Common::Point &pt);
+ virtual void SHblitFrom(const Graphics::Surface &src, const Common::Point &destPos, const Common::Rect &srcBounds) {
+ Graphics::ManagedSurface::blitFrom(src, srcBounds, destPos);
+ }
/**
* Draws an image frame at a given position within this surface with transparency
*/
- void transBlitFrom(const ImageFrame &src, const Common::Point &pt,
- bool flipped = false, int overrideColor = 0, int scaleVal = 256);
-
- /**
- * Draws a surface at a given position within this surface with transparency
- */
- void transBlitFrom(const Surface &src, const Common::Point &pt,
- bool flipped = false, int overrideColor = 0, int scaleVal = 256);
+ void SHtransBlitFrom(const ImageFrame &src, const Common::Point &pt,
+ bool flipped = false, int overrideColor = 0, int scaleVal = SCALE_THRESHOLD);
/**
- * Draws a surface at a given position within this surface with transparency
+ * Draws an image frame at a given position within this surface with transparency
*/
- void transBlitFrom(const Graphics::Surface &src, const Common::Point &pt,
- bool flipped = false, int overrideColor = 0, int scaleVal = 256);
+ void SHtransBlitFrom(const Graphics::Surface &src, const Common::Point &pt,
+ bool flipped = false, int overrideColor = 0, int scaleVal = SCALE_THRESHOLD);
/**
* Fill a given area of the surface with a given color
*/
- void fillRect(int x1, int y1, int x2, int y2, uint color);
-
- /**
- * Fill a given area of the surface with a given color
- */
- virtual void fillRect(const Common::Rect &r, uint color);
-
- void fill(uint color);
-
- /**
- * Clear the surface
- */
- void clear();
+ virtual void SHfillRect(const Common::Rect &r, uint color) {
+ Graphics::ManagedSurface::fillRect(r, color);
+ }
/**
- * Free the underlying surface
+ * Return the width of the surface
*/
- void free();
-
+ virtual uint16 width() const { return this->w; }
+
/**
- * Returns true if the surface is empty
+ * Return the height of the surface
*/
- bool empty() const { return _surface.getPixels() == nullptr; }
+ virtual uint16 height() const { return this->h; }
/**
- * Set the pixels for the surface to an existing data block
+ * Draws the given string into the back buffer using the images stored in _font
*/
- void setPixels(byte *pixels, int width, int height, Graphics::PixelFormat format);
-
+ void writeString(const Common::String &str, const Common::Point &pt, uint overrideColor);
+
/**
- * Draws the given string into the back buffer using the images stored in _font
+ * Draws a fancy version of the given string at the given position
*/
- virtual void writeString(const Common::String &str, const Common::Point &pt, uint overrideColor);
void writeFancyString(const Common::String &str, const Common::Point &pt, uint overrideColor1, uint overrideColor2);
-
- inline virtual uint16 w() const { return _surface.w; }
- inline virtual uint16 h() const { return _surface.h; }
- inline const byte *getPixels() const { return (const byte *)_surface.getPixels(); }
- inline byte *getPixels() { return (byte *)_surface.getPixels(); }
- inline byte *getBasePtr(int x, int y) { return (byte *)_surface.getBasePtr(x, y); }
- inline const byte *getBasePtr(int x, int y) const { return (const byte *)_surface.getBasePtr(x, y); }
- inline Graphics::Surface &getRawSurface() { return _surface; }
- inline void hLine(int x, int y, int x2, uint color) { _surface.hLine(x, y, x2, color); }
- inline void vLine(int x, int y, int y2, uint color) { _surface.vLine(x, y, y2, color); }
};
} // End of namespace Sherlock
diff --git a/engines/sherlock/tattoo/tattoo_darts.cpp b/engines/sherlock/tattoo/tattoo_darts.cpp
index 512358933d..cbc3ea1fe8 100644
--- a/engines/sherlock/tattoo/tattoo_darts.cpp
+++ b/engines/sherlock/tattoo/tattoo_darts.cpp
@@ -83,7 +83,9 @@ void Darts::playDarts(GameType gameType) {
int numHits = 0;
bool gameOver = false;
bool done = false;
- const char *const NUM_HITS_STR[3] = { "a", FIXED(Double), FIXED(Triple) };
+
+ // Set the game mode
+ _gameType = gameType;
screen.setFont(7);
_spacing = screen.fontHeight() + 2;
@@ -161,51 +163,84 @@ void Darts::playDarts(GameType gameType) {
// Show scores
showStatus(playerNum);
- screen._backBuffer2.blitFrom(screen._backBuffer1, Common::Point(_dartInfo.left, _dartInfo.top - 1),
+ screen._backBuffer2.SHblitFrom(screen._backBuffer1, Common::Point(_dartInfo.left, _dartInfo.top - 1),
Common::Rect(_dartInfo.left, _dartInfo.top - 1, _dartInfo.right, _dartInfo.bottom - 1));
- screen.print(Common::Point(_dartInfo.left, _dartInfo.top), 0, "%s # %d", FIXED(Dart), idx + 1);
+ screen.print(Common::Point(_dartInfo.left, _dartInfo.top), 0, FIXED(DartsCurrentDart), idx + 1);
if (_gameType == GAME_301) {
- if (_vm->getLanguage() == Common::FR_FRA)
- screen.print(Common::Point(_dartInfo.left, _dartInfo.top + _spacing), 0,
- "%s %s: %d", FIXED(Scored), FIXED(Points), lastDart);
- else
- screen.print(Common::Point(_dartInfo.left, _dartInfo.top + _spacing), 0,
- "%s %d %s", FIXED(Scored), lastDart, FIXED(Points));
+ // "Scored x points"
+ Common::String scoredPoints;
+
+ // original treated 1 point and multiple points the same. Wrote "Scored 1 points"
+ if (lastDart == 1) {
+ scoredPoints = Common::String::format(FIXED(DartsScoredPoint), lastDart);
+ } else {
+ scoredPoints = Common::String::format(FIXED(DartsScoredPoints), lastDart);
+ }
+
+ screen.print(Common::Point(_dartInfo.left, _dartInfo.top + _spacing), 0, "%s", scoredPoints.c_str());
} else {
- if (lastDart != 25)
- screen.print(Common::Point(_dartInfo.left, _dartInfo.top + _spacing), 0,
- "%s %s %d", FIXED(Hit), NUM_HITS_STR[numHits - 1], lastDart);
- else
- screen.print(Common::Point(_dartInfo.left, _dartInfo.top + _spacing), 0,
- "%s %s %s", FIXED(Hit), NUM_HITS_STR[numHits - 1], FIXED(Bullseye));
+ Common::String hitText;
+
+ if (lastDart != 25) {
+ // Regular hit
+ switch (numHits) {
+ case 1: // "Hit a X"
+ hitText = Common::String::format(FIXED(DartsHitSingle), lastDart);
+ break;
+ case 2: // "Hit double X"
+ hitText = Common::String::format(FIXED(DartsHitDouble), lastDart);
+ break;
+ case 3: // "Hit triple X"
+ hitText = Common::String::format(FIXED(DartsHitTriple), lastDart);
+ break;
+ default:
+ break;
+ }
+ } else {
+ // Bullseye
+ switch (numHits) {
+ case 1:
+ hitText = Common::String(FIXED(DartsHitSingleBullseye));
+ break;
+ case 2:
+ hitText = Common::String(FIXED(DartsHitDoubleBullseye));
+ break;
+ case 3:
+ hitText = Common::String(FIXED(DartsHitTripleBullseye));
+ break;
+ default:
+ break;
+ }
+ }
+ screen.print(Common::Point(_dartInfo.left, _dartInfo.top + _spacing), 0, "%s", hitText.c_str());
}
if (score != 0 && playerNum == 0 && !gameOver)
screen.print(Common::Point(_dartInfo.left, _dartInfo.top + _spacing * 3), 0,
- "%s", FIXED(PressAKey));
+ "%s", FIXED(DartsPressKey));
if (gameOver) {
screen.print(Common::Point(_dartInfo.left, _dartInfo.top + _spacing * 3),
- 0, "%s", FIXED(GameOver));
+ 0, "%s", FIXED(DartsGameOver));
if (playerNum == 0) {
screen.print(Common::Point(_dartInfo.left, _dartInfo.top + _spacing * 4), 0,
- "%s %s", FIXED(Holmes), FIXED(Wins));
+ FIXED(DartsWins), FIXED(DartsPlayerHolmes));
_vm->setFlagsDirect(531);
} else {
screen.print(Common::Point(_dartInfo.left, _dartInfo.top + _spacing * 4), 0,
- "%s %s!", _opponent.c_str(), FIXED(Wins));
+ FIXED(DartsWins), _opponent.c_str());
_vm->setFlagsDirect(530);
}
screen.print(Common::Point(_dartInfo.left, _dartInfo.top + _spacing * 5), 0,
- "%s", FIXED(PressAKey));
+ "%s", FIXED(DartsPressKey));
done = true;
idx = 10;
} else if (_gameType == GAME_301 && score < 0) {
screen.print(Common::Point(_dartInfo.left, _dartInfo.top + _spacing * 2), 0,
- "%s!", FIXED(Busted));
+ "%s!", FIXED(DartsBusted));
// End turn
idx = 10;
@@ -224,13 +259,19 @@ void Darts::playDarts(GameType gameType) {
done = true;
break;
}
+ // Wait for keypress
+ do {
+ events.pollEventsAndWait();
+ events.setButtonState();
+ } while (!_vm->shouldQuit() && !events.kbHit() && !events._pressed);
} else {
- events.wait(20);
+ events.wait(40);
}
- screen._backBuffer1.blitFrom(screen._backBuffer2, Common::Point(_dartInfo.left, _dartInfo.top - 1),
+ // Clears the status part of the board
+ screen._backBuffer1.SHblitFrom(screen._backBuffer2, Common::Point(_dartInfo.left, _dartInfo.top - 1),
Common::Rect(_dartInfo.left, _dartInfo.top - 1, _dartInfo.right, _dartInfo.bottom - 1));
- screen.blitFrom(screen._backBuffer1);
+ screen.SHblitFrom(screen._backBuffer1);
}
playerNum ^= 1;
@@ -238,9 +279,9 @@ void Darts::playDarts(GameType gameType) {
++_roundNum;
if (!done) {
- screen._backBuffer2.blitFrom((*_dartBoard)[0], Common::Point(0, 0));
- screen._backBuffer1.blitFrom(screen._backBuffer2);
- screen.blitFrom(screen._backBuffer2);
+ screen._backBuffer2.SHblitFrom((*_dartBoard)[0], Common::Point(0, 0));
+ screen._backBuffer1.SHblitFrom(screen._backBuffer2);
+ screen.SHblitFrom(screen._backBuffer2);
}
}
@@ -303,7 +344,7 @@ void Darts::initDarts() {
}
}
- _opponent = FIXED(Jock);
+ _opponent = FIXED(DartsPlayerJock);
}
void Darts::loadDarts() {
@@ -327,9 +368,9 @@ void Darts::loadDarts() {
delete stream;
// Load the initial background
- screen._backBuffer1.blitFrom((*_dartBoard)[0], Common::Point(0, 0));
- screen._backBuffer2.blitFrom(screen._backBuffer1);
- screen.blitFrom(screen._backBuffer1);
+ screen._backBuffer1.SHblitFrom((*_dartBoard)[0], Common::Point(0, 0));
+ screen._backBuffer2.SHblitFrom(screen._backBuffer1);
+ screen.SHblitFrom(screen._backBuffer1);
}
void Darts::closeDarts() {
@@ -346,7 +387,7 @@ void Darts::showNames(int playerNum) {
byte color;
color = playerNum == 0 ? PLAYER_COLOR : DART_COLOR_FORE;
- screen.print(Common::Point(STATUS_INFO_X, STATUS_INFO_Y), 0, "%s", FIXED(Holmes));
+ screen.print(Common::Point(STATUS_INFO_X, STATUS_INFO_Y), 0, "%s", FIXED(DartsPlayerHolmes));
screen._backBuffer1.fillRect(Common::Rect(STATUS_INFO_X, STATUS_INFO_Y + _spacing + 1,
STATUS_INFO_X + 50, STATUS_INFO_Y + _spacing + 3), color);
screen.fillRect(Common::Rect(STATUS_INFO_X, STATUS_INFO_Y + _spacing + 1,
@@ -359,14 +400,14 @@ void Darts::showNames(int playerNum) {
screen.fillRect(Common::Rect(STATUS2_INFO_X, STATUS_INFO_Y + _spacing + 1,
STATUS2_INFO_X + 50, STATUS_INFO_Y + _spacing + 3), color);
- screen._backBuffer2.blitFrom(screen._backBuffer1);
+ screen._backBuffer2.SHblitFrom(screen._backBuffer1);
}
void Darts::showStatus(int playerNum) {
Screen &screen = *_vm->_screen;
- const char *const CRICKET_SCORE_NAME[7] = { "20", "19", "18", "17", "16", "15", FIXED(Bull) };
+ const char *const CRICKET_SCORE_NAME[7] = { "20", "19", "18", "17", "16", "15", FIXED(DartsBull) };
- screen._backBuffer1.blitFrom(screen._backBuffer2, Common::Point(STATUS_INFO_X, STATUS_INFO_Y + 10),
+ screen._backBuffer1.SHblitFrom(screen._backBuffer2, Common::Point(STATUS_INFO_X, STATUS_INFO_Y + 10),
Common::Rect(STATUS_INFO_X, STATUS_INFO_Y + 10, STATUS_INFO_X + STATUS_INFO_WIDTH,
STATUS_INFO_Y + STATUS_INFO_HEIGHT - 10));
screen.print(Common::Point(STATUS_INFO_X + 30, STATUS_INFO_Y + _spacing + 4), 0, "%d", _score1);
@@ -374,10 +415,15 @@ void Darts::showStatus(int playerNum) {
screen.print(Common::Point(STATUS2_INFO_X + 30, STATUS_INFO_Y + _spacing + 4), 0, "%d", _score2);
int temp = (_gameType == GAME_CRICKET) ? STATUS_INFO_Y + 10 * _spacing + 5 : STATUS_INFO_Y + 55;
- screen.print(Common::Point(STATUS_INFO_X, temp), 0, "%s: %d", FIXED(Round), _roundNum);
+
+ // "Round: x"
+ Common::String dartsRoundStatus = Common::String::format(FIXED(DartsCurrentRound), _roundNum);
+ screen.print(Common::Point(STATUS_INFO_X, temp), 0, "%s", dartsRoundStatus.c_str());
if (_gameType == GAME_301) {
- screen.print(Common::Point(STATUS_INFO_X, STATUS_INFO_Y + 75), 0, "%s: %d", FIXED(TurnTotal), _roundScore);
+ // "Turn Total: x"
+ Common::String dartsTotalPoints = Common::String::format(FIXED(DartsCurrentTotalPoints), _roundScore);
+ screen.print(Common::Point(STATUS_INFO_X, STATUS_INFO_Y + 75), 0, "%s", dartsTotalPoints.c_str());
} else {
// Show cricket scores
for (int x = 0; x < 7; ++x) {
@@ -402,7 +448,7 @@ void Darts::showStatus(int playerNum) {
}
}
- screen.blitFrom(screen._backBuffer1, Common::Point(STATUS_INFO_X, STATUS_INFO_Y + 10),
+ screen.SHblitFrom(screen._backBuffer1, Common::Point(STATUS_INFO_X, STATUS_INFO_Y + 10),
Common::Rect(STATUS_INFO_X, STATUS_INFO_Y + 10, STATUS_INFO_X + STATUS_INFO_WIDTH,
STATUS_INFO_Y + STATUS_INFO_HEIGHT - 10));
}
@@ -412,7 +458,7 @@ void Darts::erasePowerBars() {
// Erase the old power bars and replace them with empty ones
screen._backBuffer1.fillRect(Common::Rect(DART_BAR_VX, DART_HEIGHT_Y, DART_BAR_VX + 9, DART_HEIGHT_Y + DART_BAR_SIZE), 0);
- screen._backBuffer1.transBlitFrom((*_dartGraphics)[0], Common::Point(DART_BAR_VX - 1, DART_HEIGHT_Y - 1));
+ screen._backBuffer1.SHtransBlitFrom((*_dartGraphics)[0], Common::Point(DART_BAR_VX - 1, DART_HEIGHT_Y - 1));
screen.slamArea(DART_BAR_VX - 1, DART_HEIGHT_Y - 1, 10, DART_BAR_SIZE + 2);
}
@@ -452,7 +498,7 @@ int Darts::doPowerBar(const Common::Point &pt, byte color, int goToPower, int or
}
screen._backBuffer1.hLine(pt.x, pt.y + DART_BAR_SIZE- 1 - idx, pt.x + 8, color);
- screen._backBuffer1.transBlitFrom((*_dartGraphics)[0], Common::Point(pt.x - 1, pt.y - 1));
+ screen._backBuffer1.SHtransBlitFrom((*_dartGraphics)[0], Common::Point(pt.x - 1, pt.y - 1));
screen.slamArea(pt.x, pt.y + DART_BAR_SIZE - 1 - idx, 8, 2);
if (!(idx % 8))
@@ -499,7 +545,7 @@ int Darts::drawHand(int goToPower, int computer) {
break;
}
- screen._backBuffer1.transBlitFrom((*hands)[0], pt);
+ screen._backBuffer1.SHtransBlitFrom((*hands)[0], pt);
screen.slamArea(pt.x - 1, pt.y, _handSize.x + 1, _handSize.y);
screen.restoreBackground(Common::Rect(pt.x, pt.y, pt.x + _handSize.x, pt.y + _handSize.y));
@@ -586,7 +632,7 @@ void Darts::drawDartThrow(const Common::Point &dartPos, int computer) {
_handSize.y = hands[idx]._offset.y + hands[idx]._height;
int handCy = SHERLOCK_SCREEN_HEIGHT - _handSize.y;
- screen._backBuffer1.transBlitFrom(hands[idx], Common::Point(_handX, handCy));
+ screen._backBuffer1.SHtransBlitFrom(hands[idx], Common::Point(_handX, handCy));
screen.slamArea(_handX, handCy, _handSize.x + 1, _handSize.y);
screen.slamArea(handOCx, handOCy, handOldxSize, handOldySize);
screen.restoreBackground(Common::Rect(_handX, handCy, _handX + _handSize.x, handCy + _handSize.y));
@@ -608,7 +654,7 @@ void Darts::drawDartThrow(const Common::Point &dartPos, int computer) {
ocy = drawPos.y = cy - (*_dartGraphics)[dartNum]._height;
// Draw dart
- screen._backBuffer1.transBlitFrom((*_dartGraphics)[dartNum], drawPos);
+ screen._backBuffer1.SHtransBlitFrom((*_dartGraphics)[dartNum], drawPos);
if (drawPos.x < 0) {
xSize += drawPos.x;
@@ -630,7 +676,7 @@ void Darts::drawDartThrow(const Common::Point &dartPos, int computer) {
// Flush the erased dart area
screen.slamArea(oldDrawPos.x, oldDrawPos.y, oldxSize, oldySize);
- screen._backBuffer1.blitFrom(screen._backBuffer2, Common::Point(drawPos.x, drawPos.y),
+ screen._backBuffer1.SHblitFrom(screen._backBuffer2, Common::Point(drawPos.x, drawPos.y),
Common::Rect(drawPos.x, drawPos.y, drawPos.x + xSize, drawPos.y + ySize));
oldDrawPos.x = drawPos.x;
@@ -651,7 +697,7 @@ void Darts::drawDartThrow(const Common::Point &dartPos, int computer) {
if (oldDrawPos.x != -1)
screen.slamArea(oldDrawPos.x, oldDrawPos.y, oldxSize, oldySize);
- screen._backBuffer1.blitFrom(screen._backBuffer2, Common::Point(drawPos.x, drawPos.y),
+ screen._backBuffer1.SHblitFrom(screen._backBuffer2, Common::Point(drawPos.x, drawPos.y),
Common::Rect(drawPos.x, drawPos.y, drawPos.x + xSize, drawPos.y + ySize));
cx = dartPos.x;
@@ -677,7 +723,7 @@ void Darts::drawDartThrow(const Common::Point &dartPos, int computer) {
ocx = drawPos.x = cx - (*_dartGraphics)[dartNum]._width / 2;
ocy = drawPos.y = cy - (*_dartGraphics)[dartNum]._height;
- screen._backBuffer1.transBlitFrom((*_dartGraphics)[dartNum], Common::Point(drawPos.x, drawPos.y));
+ screen._backBuffer1.SHtransBlitFrom((*_dartGraphics)[dartNum], Common::Point(drawPos.x, drawPos.y));
if (drawPos.x < 0) {
xSize += drawPos.x;
@@ -699,7 +745,7 @@ void Darts::drawDartThrow(const Common::Point &dartPos, int computer) {
screen.slamArea(oldDrawPos.x, oldDrawPos.y, oldxSize, oldySize);
if (idx != 23)
- screen._backBuffer1.blitFrom(screen._backBuffer2, drawPos,
+ screen._backBuffer1.SHblitFrom(screen._backBuffer2, drawPos,
Common::Rect(drawPos.x, drawPos.y, drawPos.x + xSize, drawPos.y + ySize)); // erase dart
events.wait(1);
@@ -716,8 +762,8 @@ void Darts::drawDartThrow(const Common::Point &dartPos, int computer) {
ySize = (*_dartGraphics)[dartNum]._height;
// Draw final dart on the board
- screen._backBuffer1.transBlitFrom((*_dartGraphics)[dartNum], Common::Point(ocx, ocy));
- screen._backBuffer2.transBlitFrom((*_dartGraphics)[dartNum], Common::Point(ocx, ocy));
+ screen._backBuffer1.SHtransBlitFrom((*_dartGraphics)[dartNum], Common::Point(ocx, ocy));
+ screen._backBuffer2.SHtransBlitFrom((*_dartGraphics)[dartNum], Common::Point(ocx, ocy));
screen.slamArea(ocx, ocy, xSize, ySize);
}
@@ -887,13 +933,16 @@ int Darts::throwDart(int dartNum, int computer) {
events.clearEvents();
erasePowerBars();
- screen.print(Common::Point(_dartInfo.left, _dartInfo.top), 0, "%s # %d", FIXED(Dart), dartNum);
+
+ // "Dart # x"
+ Common::String currentDart = Common::String::format(FIXED(DartsCurrentDart), dartNum);
+ screen.print(Common::Point(_dartInfo.left, _dartInfo.top), 0, "%s", currentDart.c_str());
drawDartsLeft(dartNum, computer);
if (!computer) {
- screen.print(Common::Point(_dartInfo.left, _dartInfo.top + _spacing), 0, "%s", FIXED(HitAKey));
- screen.print(Common::Point(_dartInfo.left, _dartInfo.top + _spacing * 2), 0, "%s", FIXED(ToStart));
+ screen.print(Common::Point(_dartInfo.left, _dartInfo.top + _spacing), 0, "%s", FIXED(DartsStartPressKey1));
+ screen.print(Common::Point(_dartInfo.left, _dartInfo.top + _spacing * 2), 0, "%s", FIXED(DartsStartPressKey2));
}
if (!computer) {
@@ -907,9 +956,9 @@ int Darts::throwDart(int dartNum, int computer) {
}
drawDartsLeft(dartNum + 1, computer);
- screen._backBuffer1.blitFrom(screen._backBuffer2, Common::Point(_dartInfo.left, _dartInfo.top - 1),
+ screen._backBuffer1.SHblitFrom(screen._backBuffer2, Common::Point(_dartInfo.left, _dartInfo.top - 1),
Common::Rect(_dartInfo.left, _dartInfo.top - 1, _dartInfo.right, _dartInfo.bottom - 1));
- screen.blitFrom(screen._backBuffer1, Common::Point(_dartInfo.left, _dartInfo.top - 1),
+ screen.SHblitFrom(screen._backBuffer1, Common::Point(_dartInfo.left, _dartInfo.top - 1),
Common::Rect(_dartInfo.left, _dartInfo.top - 1, _dartInfo.right, _dartInfo.bottom - 1));
if (computer) {
@@ -931,7 +980,7 @@ int Darts::throwDart(int dartNum, int computer) {
height = 101 - height;
// Copy power bars to the secondary back buffer
- screen._backBuffer2.blitFrom(screen._backBuffer1, Common::Point(DART_BAR_VX - 1, DART_HEIGHT_Y - 1),
+ screen._backBuffer2.SHblitFrom(screen._backBuffer1, Common::Point(DART_BAR_VX - 1, DART_HEIGHT_Y - 1),
Common::Rect(DART_BAR_VX - 1, DART_HEIGHT_Y - 1, DART_BAR_VX - 1 + 10,
DART_HEIGHT_Y - 1 + DART_BAR_SIZE + 2));
@@ -975,14 +1024,14 @@ void Darts::drawDartsLeft(int dartNum, int computer) {
const int DART_X2[3] = { 393, 441, 502 };
const int DART_Y2[3] = { 373, 373, 373 };
- screen._backBuffer1.blitFrom(screen._backBuffer2, Common::Point(DART_X1[0], DART_Y1[0]),
+ screen._backBuffer1.SHblitFrom(screen._backBuffer2, Common::Point(DART_X1[0], DART_Y1[0]),
Common::Rect(DART_X1[0], DART_Y1[0], SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT));
for (int idx = 2; idx >= dartNum - 1; --idx) {
if (computer)
- screen._backBuffer1.transBlitFrom((*_dartsLeft)[idx + 3], Common::Point(DART_X2[idx], DART_Y2[idx]));
+ screen._backBuffer1.SHtransBlitFrom((*_dartsLeft)[idx + 3], Common::Point(DART_X2[idx], DART_Y2[idx]));
else
- screen._backBuffer1.transBlitFrom((*_dartsLeft)[idx], Common::Point(DART_X1[idx], DART_Y1[idx]));
+ screen._backBuffer1.SHtransBlitFrom((*_dartsLeft)[idx], Common::Point(DART_X1[idx], DART_Y1[idx]));
}
screen.slamArea(DART_X1[0], DART_Y1[0], SHERLOCK_SCREEN_WIDTH - DART_X1[0], SHERLOCK_SCREEN_HEIGHT - DART_Y1[0]);
diff --git a/engines/sherlock/tattoo/tattoo_fixed_text.cpp b/engines/sherlock/tattoo/tattoo_fixed_text.cpp
index 38acd78aba..e41cb1f5eb 100644
--- a/engines/sherlock/tattoo/tattoo_fixed_text.cpp
+++ b/engines/sherlock/tattoo/tattoo_fixed_text.cpp
@@ -66,25 +66,27 @@ static const char *const fixedTextEN[] = {
"Search Backwards",
"Search Forwards",
"Text Not Found !",
-
+ // Darts
"Holmes",
"Jock",
"Bull",
- "Round",
- "Turn Total",
- "Dart",
- "to start",
+ "Round: %d",
+ "Turn Total: %d",
+ "Dart # %d",
"Hit a key",
+ "To start",
"Press a key",
- "bullseye", // ??
- "GAME OVER",
- "BUSTED",
- "Wins",
- "Scored",
- "points",
- "Hit %s %d",
- "Double",
- "Triple",
+ "GAME OVER!",
+ "BUSTED!",
+ "%s Wins",
+ "Scored %d point", // original: treated 1 point and multiple points the same ("Scored 1 points")
+ "Scored %d points",
+ "Hit a %d",
+ "Hit double %d",
+ "Hit triple %d",
+ "Hit a bullseye",
+ "Hit double bullseye",
+ "Hit triple bullseye",
"Apply",
"Water",
@@ -106,6 +108,8 @@ static const char *const fixedTextEN[] = {
"No",
"Enter Password",
"Going East", // correct password, was not and should not to be translated
+ "Watson's Journal",
+ "Journal saved as journal.txt",
// SH2: People names
"Sherlock Holmes",
"Dr. Watson",
@@ -235,7 +239,7 @@ static const char *const fixedTextDE[] = {
"Tagebuch",
"Tasche",
"Optionen",
- "L\224osen",
+ "L\224sen",
"mit",
"Keine Wirkung...",
"Diese Person wei\341 im Augenblick nichts zu berichten.",
@@ -243,31 +247,33 @@ static const char *const fixedTextDE[] = {
"Seite %d",
"Schlie\341en",
- "Lessen", // <--
- "In Datei sichern",
+ "Durchsuchen", // original: "Lessen"
+ "In Datei sichern", // original: "Speichern"
"Suche abbrechen",
"R\201ckw\204rts suchen ",
- "Vorwarts suchen ",
+ "Vorw\204rts suchen ",
"Text nicht gefunden",
-
+ // Darts
"Holmes",
"Jock",
"Bull",
"Runde: %d",
"Gesamt: %d",
"Pfeil # %d",
- "zum Starten",
"Taste dr\201cken",
+ "zum Starten",
"Taste dr\201cken",
- "Bullseye", // ??
"SPIEL BEENDET!",
"VERLOREN!",
- "Gewinnt!", // "Holmes Gewinnt!", "%s Gewinnt!"
+ "%s gewinnt!", // "Holmes Gewinnt!", "%s Gewinnt!", original: "%s Gewinnt!"
+ "Erzielte %d Punkt", // original: treated 1 point and multiple points the same ("Scored 1 points")
"Erzielte %d Punkte",
- "Punkte", // ??
- "Treffer %s %d",
- "Doppel",
- "Dreifach",
+ "%d getroffen", // original: "Treffer %s %d"
+ "Doppel %d getroffen", // original: see above
+ "Dreifach %d getroffen", // original: see above
+ "Bullseye getroffen",
+ "Doppel Bullseye getroffen",
+ "Dreifach Bullseye getroffen",
"Benutze",
"Wasser",
@@ -289,6 +295,8 @@ static const char *const fixedTextDE[] = {
"Nein",
"Pa\341wort eingeben",
"Going East", // correct password, was not and should not to be translated
+ "Watsons Tagebuch",
+ "Journal gespeichert als journal.txt",
// SH2: People names
"Sherlock Holmes", // note: People names were not translated in the German interpreter
"Dr. Watson",
@@ -426,31 +434,33 @@ static const char *const fixedTextFR[] = {
"Page %d",
"Fermer",
- "Lessen", // <--
+ "Rechercher",
"Sauvegarder",
"Annuler ",
"Chercher avant",
"Chercher apr\212s",
"Texte introuvable !",
-
+ // Darts
"Holmes",
"Jock",
"Bull",
"Tour: %d",
"Total: %d",
"Fl\202chette # %d",
- "pour commencer",
"Appuyez sur C",
+ "pour commencer",
"Appuyez sur C",
- "Bullseye", // ??
"FIN DE LA PARTIE!", // original: "Fin de la partie!"
"FIASCO!",
- "Gagnant!", // "Holmes Gagnant!", "%s Gagnant!"
- "Total des points: %d",
- "Punkte", // ??
- "Treffer %s %d",
- "double",
- "triple",
+ "%s a gagn\202!", // "Holmes Gagnant!", "%s Gagnant!"
+ "Rapporte %d point", // original: treated 1 point and multiple points the same ("Scored 1 points")
+ "Rapporte %d points", // original: Total des points: %d",
+ "Touche un %d", // original: ???
+ "Touche double %d",
+ "Touche triple %d",
+ "Touche le Bullseye",
+ "Touche double Bullseye",
+ "Touche triple Bullseye",
"Mouillez",
"Puis",
@@ -472,6 +482,8 @@ static const char *const fixedTextFR[] = {
"Non",
"Entrez le mot de passe",
"Going East", // correct password, was not and should not to be translated
+ "Journal de Watson",
+ "Journal enregistree comme journal.txt",
// SH2: People names
"Sherlock Holmes",
"Dr. Watson",
@@ -610,7 +622,7 @@ static const char *const fixedTextES[] = {
"P\240gina %d",
"Cerrar Diario",
- "Lessen", // <-- not included?!?!
+ "Buscar en Diario",
"Salvar en Archivo",
"Detener B\243squeda",
"Buscar Hacia Atr\240s",
@@ -623,18 +635,20 @@ static const char *const fixedTextES[] = {
"Vuelta: %d",
"Total del Turno: %d",
"Dardo # %d",
- "para empezar",
"Pulsa una tecla",
+ "para empezar",
"Pulsa una tecla",
- "Golpe %s ojo de buey", // ??
"FIN DE LA PARTIDA!",
"ROTO!",
- "Gana!", // "Holmes Gana!", "%s Gana!"
+ "%s gana!", // "Holmes Gana!", "%s Gana!", original: "%s Gana!"
+ "Puntuado %d punto", // original: treated 1 point and multiple points the same ("Scored 1 points")
"Puntuado %d puntos",
- "puntos", // ??
- "Golpe %s %d",
- "doble",
- "triple",
+ "Golpe un %d",
+ "Gople doble %d",
+ "Gople triple %d",
+ "Golpe un ojo de buey",
+ "Gople doble ojo de buey",
+ "Gople triple ojo de buey",
"aplicar",
"Agua",
@@ -656,6 +670,8 @@ static const char *const fixedTextES[] = {
"Non",
"Introducir Palabra Clave",
"Vas al Este", // correct password, was translated in Spanish version (???)
+ "Diario de Watson",
+ "Diario guarda como journal.txt",
// SH2: People names
"Sherlock Holmes",
"Dr. Watson",
@@ -755,7 +771,6 @@ static const char *const fixedTextES[] = {
"Cochero"
};
-// TODO: There also was a Spanish version of Sherlock Holmes 2
static const FixedTextLanguageEntry fixedTextLanguages[] = {
{ Common::DE_DEU, fixedTextDE },
{ Common::ES_ESP, fixedTextES },
diff --git a/engines/sherlock/tattoo/tattoo_fixed_text.h b/engines/sherlock/tattoo/tattoo_fixed_text.h
index 48d237db3c..7dbe13bbb3 100644
--- a/engines/sherlock/tattoo/tattoo_fixed_text.h
+++ b/engines/sherlock/tattoo/tattoo_fixed_text.h
@@ -68,24 +68,26 @@ enum FixedTextId {
kFixedText_SearchForwards,
kFixedText_TextNotFound,
- kFixedText_Holmes,
- kFixedText_Jock,
- kFixedText_Bull,
- kFixedText_Round,
- kFixedText_TurnTotal,
- kFixedText_Dart,
- kFixedText_ToStart,
- kFixedText_HitAKey,
- kFixedText_PressAKey,
- kFixedText_Bullseye,
- kFixedText_GameOver,
- kFixedText_Busted,
- kFixedText_Wins,
- kFixedText_Scored,
- kFixedText_Points,
- kFixedText_Hit,
- kFixedText_Double,
- kFixedText_Triple,
+ kFixedText_DartsPlayerHolmes,
+ kFixedText_DartsPlayerJock,
+ kFixedText_DartsBull,
+ kFixedText_DartsCurrentRound,
+ kFixedText_DartsCurrentTotalPoints,
+ kFixedText_DartsCurrentDart,
+ kFixedText_DartsStartPressKey1,
+ kFixedText_DartsStartPressKey2,
+ kFixedText_DartsPressKey,
+ kFixedText_DartsGameOver,
+ kFixedText_DartsBusted,
+ kFixedText_DartsWins,
+ kFixedText_DartsScoredPoint,
+ kFixedText_DartsScoredPoints,
+ kFixedText_DartsHitSingle,
+ kFixedText_DartsHitDouble,
+ kFixedText_DartsHitTriple,
+ kFixedText_DartsHitSingleBullseye,
+ kFixedText_DartsHitDoubleBullseye,
+ kFixedText_DartsHitTripleBullseye,
kFixedText_Apply,
kFixedText_Water,
@@ -107,6 +109,8 @@ enum FixedTextId {
kFixedText_No,
kFixedText_EnterPassword,
kFixedText_CorrectPassword,
+ kFixedText_WatsonsJournal,
+ kFixedText_JournalSaved,
// SH2: People names
kFixedText_People_SherlockHolmes,
kFixedText_People_DrWatson,
diff --git a/engines/sherlock/tattoo/tattoo_journal.cpp b/engines/sherlock/tattoo/tattoo_journal.cpp
index e836cca620..918887f320 100644
--- a/engines/sherlock/tattoo/tattoo_journal.cpp
+++ b/engines/sherlock/tattoo/tattoo_journal.cpp
@@ -20,6 +20,7 @@
*
*/
+#include "common/savefile.h"
#include "sherlock/tattoo/tattoo_journal.h"
#include "sherlock/tattoo/tattoo_fixed_text.h"
#include "sherlock/tattoo/tattoo_scene.h"
@@ -64,7 +65,7 @@ void TattooJournal::show() {
delete stream;
// Set screen to black, and set background
- screen._backBuffer1.blitFrom((*_journalImages)[0], Common::Point(0, 0));
+ screen._backBuffer1.SHblitFrom((*_journalImages)[0], Common::Point(0, 0));
screen.empty();
screen.setPalette(palette);
@@ -130,7 +131,7 @@ void TattooJournal::handleKeyboardEvents() {
events.warpMouse(Common::Point(r.left + r.width() / 3 - 10, r.top + screen.fontHeight() + 2));
} else {
if (_selector == JH_CLOSE)
- _selector = JH_PRINT;
+ _selector = JH_SAVE;
else
--_selector;
@@ -232,7 +233,7 @@ void TattooJournal::handleKeyboardEvents() {
if (_selector == JH_NONE) {
events.warpMouse(Common::Point(r.left + r.width() / 3 - 10, r.top + screen.fontHeight() + 2));
} else {
- if (_selector == JH_PRINT)
+ if (_selector == JH_SAVE)
_selector = JH_NONE;
else
++_selector;
@@ -378,8 +379,13 @@ void TattooJournal::handleButtons() {
break;
}
- case JH_PRINT:
- // Print Journal - not implemented in ScummVM
+ case JH_SAVE:
+ // Save journal to file
+ disableControls();
+ saveJournal();
+ drawFrame();
+ drawJournal(0, 0);
+ screen.slamArea(0, 0, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT);
break;
default:
@@ -455,7 +461,7 @@ void TattooJournal::loadLocations() {
void TattooJournal::drawFrame() {
Screen &screen = *_vm->_screen;
- screen._backBuffer1.blitFrom((*_journalImages)[0], Common::Point(0, 0));
+ screen._backBuffer1.SHblitFrom((*_journalImages)[0], Common::Point(0, 0));
drawControls(0);
}
@@ -480,10 +486,10 @@ void TattooJournal::drawControls(int mode) {
screen._backBuffer1.fillRect(inner, MENU_BACKGROUND);
// Draw the four corners of the info box
- screen._backBuffer1.transBlitFrom(images[0], Common::Point(r.left, r.top));
- screen._backBuffer1.transBlitFrom(images[1], Common::Point(r.right - images[1]._width, r.top));
- screen._backBuffer1.transBlitFrom(images[1], Common::Point(r.left, r.bottom - images[1]._height));
- screen._backBuffer1.transBlitFrom(images[1], Common::Point(r.right - images[1]._width, r.bottom - images[1]._height));
+ screen._backBuffer1.SHtransBlitFrom(images[0], Common::Point(r.left, r.top));
+ screen._backBuffer1.SHtransBlitFrom(images[1], Common::Point(r.right - images[1]._width, r.top));
+ screen._backBuffer1.SHtransBlitFrom(images[1], Common::Point(r.left, r.bottom - images[1]._height));
+ screen._backBuffer1.SHtransBlitFrom(images[1], Common::Point(r.right - images[1]._width, r.bottom - images[1]._height));
// Draw the top of the info box
screen._backBuffer1.hLine(r.left + images[0]._width, r.top, r.right - images[0]._height, INFO_TOP);
@@ -507,8 +513,8 @@ void TattooJournal::drawControls(int mode) {
// Draw the sides of the separator bar above the scroll bar
int yp = r.top + screen.fontHeight() + 7;
- screen._backBuffer1.transBlitFrom(images[4], Common::Point(r.left, yp - 1));
- screen._backBuffer1.transBlitFrom(images[5], Common::Point(r.right - images[5]._width, yp - 1));
+ screen._backBuffer1.SHtransBlitFrom(images[4], Common::Point(r.left, yp - 1));
+ screen._backBuffer1.SHtransBlitFrom(images[5], Common::Point(r.right - images[5]._width, yp - 1));
// Draw the bar above the scroll bar
screen._backBuffer1.hLine(r.left + images[4]._width, yp, r.right - images[5]._width, INFO_TOP);
@@ -519,8 +525,8 @@ void TattooJournal::drawControls(int mode) {
// Draw the Bars separating the Journal Commands
int xp = r.right / 3;
for (int idx = 0; idx < 2; ++idx) {
- screen._backBuffer1.transBlitFrom(images[6], Common::Point(xp - 2, r.top + 1));
- screen._backBuffer1.transBlitFrom(images[7], Common::Point(xp - 2, yp - 1));
+ screen._backBuffer1.SHtransBlitFrom(images[6], Common::Point(xp - 2, r.top + 1));
+ screen._backBuffer1.SHtransBlitFrom(images[7], Common::Point(xp - 2, yp - 1));
screen._backBuffer1.hLine(xp - 1, r.top + 4, yp - 2, INFO_TOP);
screen._backBuffer1.hLine(xp, r.top + 4, yp - 2, INFO_MIDDLE);
@@ -592,7 +598,7 @@ void TattooJournal::highlightJournalControls(bool slamIt) {
}
// See if the Search was selected, but is not available
- if (_journal.empty() && (_selector == JH_SEARCH || _selector == JH_PRINT))
+ if (_journal.empty() && (_selector == JH_SEARCH || _selector == JH_SAVE))
_selector = JH_NONE;
if (_selector == JH_PAGE_LEFT && _oldSelector == JH_PAGE_RIGHT)
@@ -618,7 +624,10 @@ void TattooJournal::highlightJournalControls(bool slamIt) {
color, "%s", FIXED(SearchJournal));
xp += r.width() / 3;
- color = INFO_BOTTOM;
+ if (!_journal.empty())
+ color = (_selector == JH_SAVE) ? COMMAND_HIGHLIGHTED : INFO_TOP;
+ else
+ color = INFO_BOTTOM;
screen.gPrint(Common::Point(xp - screen.stringWidth(FIXED(SaveJournal)) / 2, r.top + 5),
color, "%s", FIXED(SaveJournal));
@@ -737,7 +746,7 @@ void TattooJournal::disableControls() {
// Print the Journal commands
int xp = r.left + r.width() / 6;
- for (int idx = 0; idx < 2; ++idx) {
+ for (int idx = 0; idx < 3; ++idx) {
screen.gPrint(Common::Point(xp - screen.stringWidth(JOURNAL_COMMANDS[idx]) / 2, r.top + 5),
INFO_BOTTOM, "%s", JOURNAL_COMMANDS[idx]);
@@ -770,7 +779,7 @@ int TattooJournal::getFindName(bool printError) {
// Backup the area under the text entry
Surface bgSurface(r.width() - 6, screen.fontHeight());
- bgSurface.blitFrom(screen._backBuffer1, Common::Point(0, 0), Common::Rect(r.left + 3, cursorY,
+ bgSurface.SHblitFrom(screen._backBuffer1, Common::Point(0, 0), Common::Rect(r.left + 3, cursorY,
r.right - 3, cursorY + screen.fontHeight()));
if (printError) {
@@ -801,7 +810,7 @@ int TattooJournal::getFindName(bool printError) {
events.clearEvents();
// Restore the text background
- screen._backBuffer1.blitFrom(bgSurface, Common::Point(r.left, cursorY));
+ screen._backBuffer1.SHblitFrom(bgSurface, Common::Point(r.left, cursorY));
// If there was a name already entered, copy it to name and display it
if (!_find.empty()) {
@@ -837,7 +846,7 @@ int TattooJournal::getFindName(bool printError) {
}
else {
// Erase cursor by restoring background and writing current text
- screen._backBuffer1.blitFrom(bgSurface, Common::Point(r.left + 3, cursorY));
+ screen._backBuffer1.SHblitFrom(bgSurface, Common::Point(r.left + 3, cursorY));
screen.gPrint(Common::Point(r.left + screen.widestChar() + 3, cursorY), COMMAND_HIGHLIGHTED, "%s", name.c_str());
screen.slamArea(r.left + 3, cursorY, r.width() - 3, screen.fontHeight());
}
@@ -880,11 +889,11 @@ int TattooJournal::getFindName(bool printError) {
} else {
if (keyState.keycode & Common::KBD_SHIFT) {
if (_selector == JH_CLOSE)
- _selector = JH_PRINT;
+ _selector = JH_SAVE;
else
--_selector;
} else {
- if (_selector == JH_PRINT)
+ if (_selector == JH_SAVE)
_selector = JH_CLOSE;
else
++_selector;
@@ -903,7 +912,7 @@ int TattooJournal::getFindName(bool printError) {
}
// Redraw the text
- screen._backBuffer1.blitFrom(bgSurface, Common::Point(r.left + 3, cursorY));
+ screen._backBuffer1.SHblitFrom(bgSurface, Common::Point(r.left + 3, cursorY));
screen.gPrint(Common::Point(r.left + screen.widestChar() + 3, cursorY), COMMAND_HIGHLIGHTED,
"%s", name.c_str());
screen.slamArea(r.left + 3, cursorY, r.right - 3, screen.fontHeight());
@@ -949,6 +958,173 @@ void TattooJournal::record(int converseNum, int statementNum, bool replyOnly) {
Journal::record(converseNum, statementNum, replyOnly);
}
+void TattooJournal::saveJournal() {
+ Talk &talk = *_vm->_talk;
+ Common::OutSaveFile *file = g_system->getSavefileManager()->openForSaving("journal.txt", false);
+ int tempIndex = _index;
+
+ _index = 0;
+ talk._converseNum = -1;
+
+ file->writeString(" ");
+ file->writeString(FIXED(WatsonsJournal));
+ file->writeString("\n\n");
+
+ // Loop through saving each page of the journal
+ do {
+ // Print a single talk file
+ Common::String text;
+ int line = 0;
+
+ // Copy all of the talk files entries into one big string
+ do {
+ if (_lines[line].hasPrefix("@")) {
+ text += Common::String(_lines[line].c_str() + 1);
+ if ((line + 1) < (int)_lines.size() && _lines[line + 1].hasPrefix("@"))
+ text += "\n";
+ else
+ text += " ";
+ } else {
+ text += _lines[line];
+ text += " ";
+
+ // Check for embedded location names embedded in comment fields,
+ // which show up as a blank line with the next line starting
+ // with a '@'. We have to add a line break here because the '@' handler
+ // previously assumes that they're always following a blank line
+
+ if ((_lines[line].empty() || _lines[line] == " ")
+ && (line + 1) < (int)_lines.size() && _lines[line + 1].hasPrefix("@"))
+ text += "\n";
+ }
+
+ ++line;
+ } while (line < (int)_lines.size());
+
+ // Now write out the text in 80 column lines
+ do {
+ if (text.size() > 80) {
+ const char *msgP = text.c_str() + 80;
+
+ if (Common::String(text.c_str(), msgP).contains("\n")) {
+ // The 80 characters contain a carriage return,
+ // so we can print out that line
+ const char *cr = strchr(text.c_str(), '\n');
+ file->writeString(Common::String(text.c_str(), cr));
+ text = Common::String(cr + 1);
+ } else {
+ // Move backwards to find a word break
+ while (*msgP != ' ')
+ --msgP;
+
+ // Write out the figured out line
+ file->writeString(Common::String(text.c_str(), msgP));
+
+ // Remove the line that was written out
+ while (*msgP == ' ')
+ ++msgP;
+ text = Common::String(msgP);
+ }
+ } else {
+ // The remainder of the string is under 80 characters.
+ // Check to see if has any line ends
+ if (text.contains("\n")) {
+ // Write out the line up to the carraige return
+ const char *cr = strchr(text.c_str(), '\n');
+ file->writeString(Common::String(text.c_str(), cr));
+ text = Common::String(cr + 1);
+ } else {
+ // Write out the final line
+ file->writeString(text);
+ text = "";
+ }
+ }
+
+ file->writeString("\n");
+ } while (!text.empty());
+
+ // Move to next talk file
+ do {
+ ++_index;
+
+ if (_index < (int)_journal.size())
+ loadJournalFile(false);
+ } while (_index < (int)_journal.size() && _lines.empty());
+
+ // Don't immediately exit if there are no loaded lines for
+ // the next page, since it's probably a stealth file and
+ // can simply be skipped
+ file->writeString("\n");
+ } while (_index < (int)_journal.size());
+
+ file->finalize();
+ delete file;
+
+ // Free up any talk file in memory
+ talk.freeTalkVars();
+
+ // Show the message for the journal having been saved
+ showSavedDialog();
+
+ // Reset the previous settings of the journal
+ _index = tempIndex;
+}
+
+void TattooJournal::showSavedDialog() {
+ TattooEngine &vm = *(TattooEngine *)_vm;
+ Events &events = *vm._events;
+ Screen &screen = *vm._screen;
+ TattooUserInterface &ui = *(TattooUserInterface *)vm._ui;
+ ImageFile &images = *ui._interfaceImages;
+ disableControls();
+
+ Common::String msg = FIXED(JournalSaved);
+ Common::Rect inner(0, 0, screen.stringWidth(msg), screen.fontHeight());
+ inner.moveTo((SHERLOCK_SCREEN_WIDTH - inner.width()) / 2,
+ (SHERLOCK_SCREEN_HEIGHT / 2) - (screen.fontHeight() / 2));
+
+ Common::Rect r = inner;
+ r.grow(10);
+
+ if (vm._transparentMenus)
+ ui.makeBGArea(r);
+ else
+ screen._backBuffer1.fillRect(r, MENU_BACKGROUND);
+
+ // Draw the four corners of the info box
+ screen._backBuffer1.transBlitFrom(images[0], Common::Point(r.left, r.top));
+ screen._backBuffer1.transBlitFrom(images[1], Common::Point(r.right - images[1]._width, r.top));
+ screen._backBuffer1.transBlitFrom(images[1], Common::Point(r.left, r.bottom - images[1]._height));
+ screen._backBuffer1.transBlitFrom(images[1], Common::Point(r.right - images[1]._width, r.bottom - images[1]._height));
+
+ // Draw the top of the info box
+ screen._backBuffer1.hLine(r.left + images[0]._width, r.top, r.right - images[0]._height, INFO_TOP);
+ screen._backBuffer1.hLine(r.left + images[0]._width, r.top + 1, r.right - images[0]._height, INFO_MIDDLE);
+ screen._backBuffer1.hLine(r.left + images[0]._width, r.top + 2, r.right - images[0]._height, INFO_BOTTOM);
+
+ // Draw the bottom of the info box
+ screen._backBuffer1.hLine(r.left + images[0]._width, r.bottom - 3, r.right - images[0]._height, INFO_TOP);
+ screen._backBuffer1.hLine(r.left + images[0]._width, r.bottom - 2, r.right - images[0]._height, INFO_MIDDLE);
+ screen._backBuffer1.hLine(r.left + images[0]._width, r.bottom - 1, r.right - images[0]._height, INFO_BOTTOM);
+
+ // Draw the left side of the info box
+ screen._backBuffer1.vLine(r.left, r.top + images[0]._height, r.bottom - images[2]._height, INFO_TOP);
+ screen._backBuffer1.vLine(r.left + 1, r.top + images[0]._height, r.bottom - images[2]._height, INFO_MIDDLE);
+ screen._backBuffer1.vLine(r.left + 2, r.top + images[0]._height, r.bottom - images[2]._height, INFO_BOTTOM);
+
+ // Draw the right side of the info box
+ screen._backBuffer1.vLine(r.right - 3, r.top + images[0]._height, r.bottom - images[2]._height, INFO_TOP);
+ screen._backBuffer1.vLine(r.right - 2, r.top + images[0]._height, r.bottom - images[2]._height, INFO_MIDDLE);
+ screen._backBuffer1.vLine(r.right - 1, r.top + images[0]._height, r.bottom - images[2]._height, INFO_BOTTOM);
+
+ // Draw the text
+ screen._backBuffer1.writeString(msg, Common::Point(inner.left, inner.top), INFO_TOP);
+ screen.slamRect(r);
+
+ // Five second pause
+ events.delay(5000, true);
+}
+
} // End of namespace Tattoo
} // End of namespace Sherlock
diff --git a/engines/sherlock/tattoo/tattoo_journal.h b/engines/sherlock/tattoo/tattoo_journal.h
index 96c1c6cab4..9f0fa1fc9b 100644
--- a/engines/sherlock/tattoo/tattoo_journal.h
+++ b/engines/sherlock/tattoo/tattoo_journal.h
@@ -31,7 +31,7 @@ namespace Sherlock {
namespace Tattoo {
enum JournalHighlight {
- JH_NONE = -1, JH_CLOSE = 0, JH_SEARCH = 1, JH_PRINT = 2,
+ JH_NONE = -1, JH_CLOSE = 0, JH_SEARCH = 1, JH_SAVE = 2,
JH_SCROLL_LEFT = 3, JH_PAGE_LEFT = 4, JH_PAGE_RIGHT = 5, JH_SCROLL_RIGHT = 6, JH_THUMBNAIL = 7
};
@@ -86,6 +86,16 @@ private:
* Get in a name to search through the journal for
*/
int getFindName(bool printError);
+
+ /**
+ * Save the journal to file
+ */
+ void saveJournal();
+
+ /**
+ * Show a message that the journal has been saved to file
+ */
+ void showSavedDialog();
public:
TattooJournal(SherlockEngine *vm);
virtual ~TattooJournal() {}
diff --git a/engines/sherlock/tattoo/tattoo_map.cpp b/engines/sherlock/tattoo/tattoo_map.cpp
index 4c7e8c8fef..0839e46260 100644
--- a/engines/sherlock/tattoo/tattoo_map.cpp
+++ b/engines/sherlock/tattoo/tattoo_map.cpp
@@ -105,7 +105,7 @@ int TattooMap::show() {
// Load the map image and draw it to the back buffer
ImageFile *map = new ImageFile("map.vgs");
screen._backBuffer1.create(SHERLOCK_SCREEN_WIDTH * 2, SHERLOCK_SCREEN_HEIGHT * 2);
- screen._backBuffer1.blitFrom((*map)[0], Common::Point(0, 0));
+ screen._backBuffer1.SHblitFrom((*map)[0], Common::Point(0, 0));
delete map;
screen.clear();
@@ -114,7 +114,7 @@ int TattooMap::show() {
// Copy the map drawn in the back buffer to the secondary back buffer
screen._backBuffer2.create(SHERLOCK_SCREEN_WIDTH * 2, SHERLOCK_SCREEN_HEIGHT * 2);
- screen._backBuffer2.blitFrom(screen._backBuffer1);
+ screen._backBuffer2.SHblitFrom(screen._backBuffer1);
// Display the built map to the screen
screen.slamArea(0, 0, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT);
@@ -148,12 +148,12 @@ int TattooMap::show() {
if (_targetScroll.x < 0)
_targetScroll.x = 0;
- if ((_targetScroll.x + SHERLOCK_SCREEN_WIDTH) > screen._backBuffer1.w())
- _targetScroll.x = screen._backBuffer1.w() - SHERLOCK_SCREEN_WIDTH;
+ if ((_targetScroll.x + SHERLOCK_SCREEN_WIDTH) > screen._backBuffer1.width())
+ _targetScroll.x = screen._backBuffer1.width() - SHERLOCK_SCREEN_WIDTH;
if (_targetScroll.y < 0)
_targetScroll.y = 0;
- if ((_targetScroll.y + SHERLOCK_SCREEN_HEIGHT) > screen._backBuffer1.h())
- _targetScroll.y = screen._backBuffer1.h() - SHERLOCK_SCREEN_HEIGHT;
+ if ((_targetScroll.y + SHERLOCK_SCREEN_HEIGHT) > screen._backBuffer1.height())
+ _targetScroll.y = screen._backBuffer1.height() - SHERLOCK_SCREEN_HEIGHT;
// Check the keyboard
if (events.kbHit()) {
@@ -166,8 +166,8 @@ int TattooMap::show() {
break;
case Common::KEYCODE_END:
- _targetScroll.x = screen._backBuffer1.w() - SHERLOCK_SCREEN_WIDTH;
- _targetScroll.y = screen._backBuffer1.h() - SHERLOCK_SCREEN_HEIGHT;
+ _targetScroll.x = screen._backBuffer1.width() - SHERLOCK_SCREEN_WIDTH;
+ _targetScroll.y = screen._backBuffer1.height() - SHERLOCK_SCREEN_HEIGHT;
break;
case Common::KEYCODE_PAGEUP:
@@ -178,8 +178,8 @@ int TattooMap::show() {
case Common::KEYCODE_PAGEDOWN:
_targetScroll.y += SHERLOCK_SCREEN_HEIGHT;
- if (_targetScroll.y > (screen._backBuffer1.h() - SHERLOCK_SCREEN_HEIGHT))
- _targetScroll.y = screen._backBuffer1.h() - SHERLOCK_SCREEN_HEIGHT;
+ if (_targetScroll.y > (screen._backBuffer1.height() - SHERLOCK_SCREEN_HEIGHT))
+ _targetScroll.y = screen._backBuffer1.height() - SHERLOCK_SCREEN_HEIGHT;
break;
case Common::KEYCODE_SPACE:
@@ -304,7 +304,7 @@ void TattooMap::drawMapIcons() {
if (_data[idx]._iconNum != -1 && _vm->readFlags(idx + 1)) {
MapEntry &mapEntry = _data[idx];
ImageFrame &img = (*_iconImages)[mapEntry._iconNum];
- screen._backBuffer1.transBlitFrom(img._frame, Common::Point(mapEntry.x - img._width / 2,
+ screen._backBuffer1.SHtransBlitFrom(img._frame, Common::Point(mapEntry.x - img._width / 2,
mapEntry.y - img._height / 2));
}
}
@@ -355,10 +355,10 @@ void TattooMap::restoreArea(const Common::Rect &bounds) {
Screen &screen = *_vm->_screen;
Common::Rect r = bounds;
- r.clip(Common::Rect(0, 0, screen._backBuffer1.w(), screen._backBuffer1.h()));
+ r.clip(Common::Rect(0, 0, screen._backBuffer1.width(), screen._backBuffer1.height()));
if (!r.isEmpty())
- screen._backBuffer1.blitFrom(screen._backBuffer2, Common::Point(r.left, r.top), r);
+ screen._backBuffer1.SHblitFrom(screen._backBuffer2, Common::Point(r.left, r.top), r);
}
void TattooMap::showCloseUp(int closeUpNum) {
@@ -407,7 +407,7 @@ void TattooMap::showCloseUp(int closeUpNum) {
screen._currentScroll.y + closeUp.y / 100 - picSize.y / 2);
restoreArea(oldBounds);
- screen._backBuffer1.transBlitFrom(pic[0], pt, false, 0, scaleVal);
+ screen._backBuffer1.SHtransBlitFrom(pic[0], pt, false, 0, scaleVal);
screen.slamRect(oldBounds);
screen.slamArea(pt.x, pt.y, picSize.x, picSize.y);
@@ -426,7 +426,7 @@ void TattooMap::showCloseUp(int closeUpNum) {
screen._currentScroll.y + SHERLOCK_SCREEN_HEIGHT / 2 - pic[0]._height / 2 + pic[0]._height);
restoreArea(oldBounds);
- screen._backBuffer1.transBlitFrom(pic[0], Common::Point(r.left, r.top));
+ screen._backBuffer1.SHtransBlitFrom(pic[0], Common::Point(r.left, r.top));
screen.slamRect(oldBounds);
screen.slamRect(r);
diff --git a/engines/sherlock/tattoo/tattoo_people.cpp b/engines/sherlock/tattoo/tattoo_people.cpp
index 0af8deff9f..65cc283b66 100644
--- a/engines/sherlock/tattoo/tattoo_people.cpp
+++ b/engines/sherlock/tattoo/tattoo_people.cpp
@@ -1042,7 +1042,7 @@ void TattooPerson::walkHolmesToNPC() {
holmes._walkDest.x = MAX(_position.x / FIXED_INT_MULTIPLIER - imgFrame.sDrawXSize(scaleVal), 0);
} else {
holmes._walkDest.x = MIN(_position.x / FIXED_INT_MULTIPLIER + imgFrame.sDrawXSize(scaleVal) * 2,
- screen._backBuffer1.w() - 1);
+ screen._backBuffer1.width() - 1);
}
// See where Holmes is with respect to the NPC (y coords)
@@ -1168,7 +1168,7 @@ void TattooPerson::centerScreenOnPerson() {
TattooUserInterface &ui = *(TattooUserInterface *)_vm->_ui;
ui._targetScroll.x = CLIP(_position.x / FIXED_INT_MULTIPLIER - SHERLOCK_SCREEN_WIDTH / 2,
- 0, screen._backBuffer1.w() - SHERLOCK_SCREEN_WIDTH);
+ 0, screen._backBuffer1.width() - SHERLOCK_SCREEN_WIDTH);
screen._currentScroll = ui._targetScroll;
// Reset the default look position to the center of the screen
@@ -1354,6 +1354,7 @@ void TattooPeople::setTalkSequence(int speaker, int sequenceNum) {
int TattooPeople::findSpeaker(int speaker) {
+ speaker &= 0x7f;
int result = People::findSpeaker(speaker);
const char *portrait = _characters[speaker]._portrait;
@@ -1477,7 +1478,7 @@ const Common::Point TattooPeople::restrictToZone(int zoneId, const Common::Point
Screen &screen = *_vm->_screen;
Common::Rect &r = scene._zones[zoneId];
- if (destPos.x < 0 || destPos.x > screen._backBuffer1.w())
+ if (destPos.x < 0 || destPos.x > screen._backBuffer1.width())
return destPos;
else if (destPos.y < r.top && r.left < destPos.x && destPos.x < r.right)
return Common::Point(destPos.x, r.top);
diff --git a/engines/sherlock/tattoo/tattoo_scene.cpp b/engines/sherlock/tattoo/tattoo_scene.cpp
index 27f37665dc..00015cb189 100644
--- a/engines/sherlock/tattoo/tattoo_scene.cpp
+++ b/engines/sherlock/tattoo/tattoo_scene.cpp
@@ -141,15 +141,15 @@ void TattooScene::drawAllShapes() {
if (obj._type == ACTIVE_BG_SHAPE && obj._misc == BEHIND) {
if (obj._quickDraw && obj._scaleVal == SCALE_THRESHOLD)
- screen._backBuffer1.blitFrom(*obj._imageFrame, obj._position);
+ screen._backBuffer1.SHblitFrom(*obj._imageFrame, obj._position);
else
- screen._backBuffer1.transBlitFrom(*obj._imageFrame, obj._position, obj._flags & OBJ_FLIPPED, 0, obj._scaleVal);
+ screen._backBuffer1.SHtransBlitFrom(*obj._imageFrame, obj._position, obj._flags & OBJ_FLIPPED, 0, obj._scaleVal);
}
}
// Draw the animation if it is behind the person
if (_activeCAnim.active() && _activeCAnim._zPlacement == BEHIND)
- screen._backBuffer1.transBlitFrom(_activeCAnim._imageFrame, _activeCAnim._position,
+ screen._backBuffer1.SHtransBlitFrom(_activeCAnim._imageFrame, _activeCAnim._position,
(_activeCAnim._flags & 4) >> 1, 0, _activeCAnim._scaleVal);
screen.resetDisplayBounds();
@@ -194,13 +194,13 @@ void TattooScene::drawAllShapes() {
if (se._shape) {
// it's a bg shape
if (se._shape->_quickDraw && se._shape->_scaleVal == SCALE_THRESHOLD)
- screen._backBuffer1.blitFrom(*se._shape->_imageFrame, se._shape->_position);
+ screen._backBuffer1.SHblitFrom(*se._shape->_imageFrame, se._shape->_position);
else
- screen._backBuffer1.transBlitFrom(*se._shape->_imageFrame, se._shape->_position,
+ screen._backBuffer1.SHtransBlitFrom(*se._shape->_imageFrame, se._shape->_position,
se._shape->_flags & OBJ_FLIPPED, 0, se._shape->_scaleVal);
} else if (se._isAnimation) {
// It's an active animation
- screen._backBuffer1.transBlitFrom(_activeCAnim._imageFrame, _activeCAnim._position,
+ screen._backBuffer1.SHtransBlitFrom(_activeCAnim._imageFrame, _activeCAnim._position,
(_activeCAnim._flags & 4) >> 1, 0, _activeCAnim._scaleVal);
} else {
// Drawing person
@@ -212,7 +212,7 @@ void TattooScene::drawAllShapes() {
if (p._tempScaleVal == SCALE_THRESHOLD) {
p._tempX += adjust.x;
- screen._backBuffer1.transBlitFrom(*p._imageFrame, Common::Point(p._tempX, p._position.y / FIXED_INT_MULTIPLIER
+ screen._backBuffer1.SHtransBlitFrom(*p._imageFrame, Common::Point(p._tempX, p._position.y / FIXED_INT_MULTIPLIER
- p.frameHeight() - adjust.y), p._walkSequences[p._sequenceNumber]._horizFlip, 0, p._tempScaleVal);
} else {
if (adjust.x) {
@@ -242,7 +242,7 @@ void TattooScene::drawAllShapes() {
++adjust.y;
}
- screen._backBuffer1.transBlitFrom(*p._imageFrame, Common::Point(p._tempX, p._position.y / FIXED_INT_MULTIPLIER
+ screen._backBuffer1.SHtransBlitFrom(*p._imageFrame, Common::Point(p._tempX, p._position.y / FIXED_INT_MULTIPLIER
- p._imageFrame->sDrawYSize(p._tempScaleVal) - adjust.y), p._walkSequences[p._sequenceNumber]._horizFlip, 0, p._tempScaleVal);
}
}
@@ -255,15 +255,15 @@ void TattooScene::drawAllShapes() {
if (obj._type == ACTIVE_BG_SHAPE && obj._misc == FORWARD) {
if (obj._quickDraw && obj._scaleVal == SCALE_THRESHOLD)
- screen._backBuffer1.blitFrom(*obj._imageFrame, obj._position);
+ screen._backBuffer1.SHblitFrom(*obj._imageFrame, obj._position);
else
- screen._backBuffer1.transBlitFrom(*obj._imageFrame, obj._position, obj._flags & OBJ_FLIPPED, 0, obj._scaleVal);
+ screen._backBuffer1.SHtransBlitFrom(*obj._imageFrame, obj._position, obj._flags & OBJ_FLIPPED, 0, obj._scaleVal);
}
}
// Draw the canimation if it is set as FORWARD
if (_activeCAnim.active() && _activeCAnim._zPlacement == FORWARD)
- screen._backBuffer1.transBlitFrom(_activeCAnim._imageFrame, _activeCAnim._position, (_activeCAnim._flags & 4) >> 1, 0, _activeCAnim._scaleVal);
+ screen._backBuffer1.SHtransBlitFrom(_activeCAnim._imageFrame, _activeCAnim._position, (_activeCAnim._flags & 4) >> 1, 0, _activeCAnim._scaleVal);
// Draw all NO_SHAPE shapes which have their flag bits clear
for (uint idx = 0; idx < _bgShapes.size(); ++idx) {
diff --git a/engines/sherlock/tattoo/tattoo_screen.cpp b/engines/sherlock/tattoo/tattoo_screen.cpp
new file mode 100644
index 0000000000..c98ae2679d
--- /dev/null
+++ b/engines/sherlock/tattoo/tattoo_screen.cpp
@@ -0,0 +1,37 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 "sherlock/tattoo/tattoo_screen.h"
+#include "sherlock/tattoo/tattoo.h"
+
+namespace Sherlock {
+
+namespace Tattoo {
+
+TattooScreen::TattooScreen(SherlockEngine *vm) : Screen(vm) {
+ _backBuffer1.create(640, 480);
+ _backBuffer2.create(640, 480);
+}
+
+} // End of namespace Tattoo
+
+} // End of namespace Sherlock
diff --git a/engines/sherlock/tattoo/tattoo_screen.h b/engines/sherlock/tattoo/tattoo_screen.h
new file mode 100644
index 0000000000..b55e9bb0dd
--- /dev/null
+++ b/engines/sherlock/tattoo/tattoo_screen.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 SHERLOCK_TATTOO_SCREEN_H
+#define SHERLOCK_TATTOO_SCREEN_H
+
+#include "sherlock/screen.h"
+
+namespace Sherlock {
+
+class SherlockEngine;
+
+namespace Tattoo {
+
+class TattooScreen : public Screen {
+public:
+ TattooScreen(SherlockEngine *vm);
+ virtual ~TattooScreen() {}
+};
+
+} // End of namespace Tattoo
+
+} // End of namespace Sherlock
+
+#endif
diff --git a/engines/sherlock/tattoo/tattoo_talk.cpp b/engines/sherlock/tattoo/tattoo_talk.cpp
index a5ada7b63e..e6b9a9627e 100644
--- a/engines/sherlock/tattoo/tattoo_talk.cpp
+++ b/engines/sherlock/tattoo/tattoo_talk.cpp
@@ -795,7 +795,10 @@ OpcodeReturn TattooTalk::cmdTalkInterruptsDisable(const byte *&str) { error("Dum
// Dummy opcode
OpcodeReturn TattooTalk::cmdTalkInterruptsEnable(const byte *&str) { error("Dummy opcode cmdTalkInterruptsEnable called"); }
-OpcodeReturn TattooTalk::cmdTurnSoundsOff(const byte *&str) { error("TODO: script opcode (cmdTurnSoundsOff)"); }
+OpcodeReturn TattooTalk::cmdTurnSoundsOff(const byte *&str) {
+ _vm->_sound->stopSound();
+ return RET_SUCCESS;
+}
OpcodeReturn TattooTalk::cmdWalkHolmesAndNPCToCAnimation(const byte *&str) {
int npcNum = *++str;
diff --git a/engines/sherlock/tattoo/tattoo_user_interface.cpp b/engines/sherlock/tattoo/tattoo_user_interface.cpp
index ee028f89c2..677a662535 100644
--- a/engines/sherlock/tattoo/tattoo_user_interface.cpp
+++ b/engines/sherlock/tattoo/tattoo_user_interface.cpp
@@ -72,7 +72,7 @@ TattooUserInterface::~TattooUserInterface() {
void TattooUserInterface::initScrollVars() {
Screen &screen = *_vm->_screen;
- _scrollSize = screen._backBuffer1.w() - SHERLOCK_SCREEN_WIDTH;
+ _scrollSize = screen._backBuffer1.width() - SHERLOCK_SCREEN_WIDTH;
_targetScroll = Common::Point(0, 0);
screen._currentScroll = Common::Point(0, 0);
}
@@ -233,7 +233,7 @@ void TattooUserInterface::doJournal() {
Common::copy(&lookupTable1[0], &lookupTable1[PALETTE_COUNT], &_lookupTable1[0]);
// Restore the scene
- screen._backBuffer1.blitFrom(screen._backBuffer2);
+ screen._backBuffer1.SHblitFrom(screen._backBuffer2);
scene.updateBackground();
screen.slamArea(screen._currentScroll.x, screen._currentScroll.y, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT);
}
@@ -727,7 +727,7 @@ void TattooUserInterface::doBgAnimEraseBackground() {
if (_mask != nullptr) {
// Since a mask is active, restore the screen from the secondary back buffer prior to applying the mask
- screen._backBuffer1.blitFrom(screen._backBuffer2, screen._currentScroll, Common::Rect(screen._currentScroll.x, 0,
+ screen._backBuffer1.SHblitFrom(screen._backBuffer2, screen._currentScroll, Common::Rect(screen._currentScroll.x, 0,
screen._currentScroll.x + SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT));
switch (scene._currentScene) {
@@ -757,7 +757,7 @@ void TattooUserInterface::doBgAnimEraseBackground() {
case 53:
if (++_maskCounter == 2) {
_maskCounter = 0;
- if (++_maskOffset.x == screen._backBuffer1.w())
+ if (++_maskOffset.x == screen._backBuffer1.width())
_maskOffset.x = 0;
}
break;
@@ -779,7 +779,7 @@ void TattooUserInterface::doBgAnimEraseBackground() {
if ((obj._type == ACTIVE_BG_SHAPE && (obj._maxFrames > 1 || obj._delta.x != 0 || obj._delta.y != 0)) ||
obj._type == HIDE_SHAPE || obj._type == REMOVE)
- screen._backBuffer1.blitFrom(screen._backBuffer2, obj._oldPosition,
+ screen._backBuffer1.SHblitFrom(screen._backBuffer2, obj._oldPosition,
Common::Rect(obj._oldPosition.x, obj._oldPosition.y, obj._oldPosition.x + obj._oldSize.x,
obj._oldPosition.y + obj._oldSize.y));
}
@@ -793,7 +793,7 @@ void TattooUserInterface::doBgAnimEraseBackground() {
Object &obj = scene._bgShapes[idx];
if (obj._type == NO_SHAPE && (obj._flags & 1) == 0) {
- screen._backBuffer1.blitFrom(screen._backBuffer2, obj._position, obj.getNoShapeBounds());
+ screen._backBuffer1.SHblitFrom(screen._backBuffer2, obj._position, obj.getNoShapeBounds());
obj._oldPosition = obj._position;
obj._oldSize = obj._noShapeSize;
@@ -870,7 +870,7 @@ void TattooUserInterface::maskArea(Common::SeekableReadStream &mask, const Commo
int pixel, len, xp, yp;
for (yp = 0; yp < ySize; ++yp) {
- byte *ptr = bb1.getBasePtr(pt.x, pt.y + yp);
+ byte *ptr = (byte *)bb1.getBasePtr(pt.x, pt.y + yp);
for (xp = 0; xp < xSize;) {
// The mask data consists of pairs of pixel/lengths, where all non-zero pixels means that the
@@ -893,7 +893,7 @@ void TattooUserInterface::makeBGArea(const Common::Rect &r) {
Screen &screen = *_vm->_screen;
for (int yp = r.top; yp < r.bottom; ++yp) {
- byte *ptr = screen._backBuffer1.getBasePtr(r.left, yp);
+ byte *ptr = (byte *)screen._backBuffer1.getBasePtr(r.left, yp);
for (int xp = r.left; xp < r.right; ++xp, ++ptr)
*ptr = _lookupTable[*ptr];
diff --git a/engines/sherlock/tattoo/widget_base.cpp b/engines/sherlock/tattoo/widget_base.cpp
index 8f0649130a..a35f4e5d74 100644
--- a/engines/sherlock/tattoo/widget_base.cpp
+++ b/engines/sherlock/tattoo/widget_base.cpp
@@ -88,7 +88,7 @@ void WidgetBase::erase() {
if (_oldBounds.width() > 0) {
// Restore the affected area from the secondary back buffer into the first one, and then copy to screen
- screen._backBuffer1.blitFrom(screen._backBuffer2, Common::Point(_oldBounds.left, _oldBounds.top), _oldBounds);
+ screen._backBuffer1.SHblitFrom(screen._backBuffer2, Common::Point(_oldBounds.left, _oldBounds.top), _oldBounds);
screen.slamRect(_oldBounds);
// Reset the old bounds so it won't be erased again
@@ -111,7 +111,7 @@ void WidgetBase::draw() {
drawBackground();
// Draw the widget onto the back buffer and then slam it to the screen
- screen._backBuffer1.transBlitFrom(_surface, Common::Point(_bounds.left, _bounds.top));
+ screen._backBuffer1.SHtransBlitFrom(_surface, Common::Point(_bounds.left, _bounds.top));
screen.slamRect(_bounds);
// Store a copy of the drawn area for later erasing
@@ -183,8 +183,8 @@ void WidgetBase::restrictToScreen() {
_bounds.moveTo(_bounds.left, 0);
if (_bounds.right > (screen._currentScroll.x + SHERLOCK_SCREEN_WIDTH))
_bounds.moveTo(screen._currentScroll.x + SHERLOCK_SCREEN_WIDTH - _bounds.width(), _bounds.top);
- if (_bounds.bottom > screen._backBuffer1.h())
- _bounds.moveTo(_bounds.left, screen._backBuffer1.h() - _bounds.height());
+ if (_bounds.bottom > screen._backBuffer1.height())
+ _bounds.moveTo(_bounds.left, screen._backBuffer1.height() - _bounds.height());
}
void WidgetBase::makeInfoArea(Surface &s) {
@@ -192,30 +192,30 @@ void WidgetBase::makeInfoArea(Surface &s) {
ImageFile &images = *ui._interfaceImages;
// Draw the four corners of the Info Box
- s.transBlitFrom(images[0], Common::Point(0, 0));
- s.transBlitFrom(images[1], Common::Point(s.w() - images[1]._width, 0));
- s.transBlitFrom(images[2], Common::Point(0, s.h() - images[2]._height));
- s.transBlitFrom(images[3], Common::Point(s.w() - images[3]._width, s.h()));
+ s.SHtransBlitFrom(images[0], Common::Point(0, 0));
+ s.SHtransBlitFrom(images[1], Common::Point(s.width() - images[1]._width, 0));
+ s.SHtransBlitFrom(images[2], Common::Point(0, s.height() - images[2]._height));
+ s.SHtransBlitFrom(images[3], Common::Point(s.width() - images[3]._width, s.height()));
// Draw the top of the Info Box
- s.hLine(images[0]._width, 0, s.w() - images[1]._width, INFO_TOP);
- s.hLine(images[0]._width, 1, s.w() - images[1]._width, INFO_MIDDLE);
- s.hLine(images[0]._width, 2, s.w() - images[1]._width, INFO_BOTTOM);
+ s.hLine(images[0]._width, 0, s.width() - images[1]._width, INFO_TOP);
+ s.hLine(images[0]._width, 1, s.width() - images[1]._width, INFO_MIDDLE);
+ s.hLine(images[0]._width, 2, s.width() - images[1]._width, INFO_BOTTOM);
// Draw the bottom of the Info Box
- s.hLine(images[0]._width, s.h()- 3, s.w() - images[1]._width, INFO_TOP);
- s.hLine(images[0]._width, s.h()- 2, s.w() - images[1]._width, INFO_MIDDLE);
- s.hLine(images[0]._width, s.h()- 1, s.w() - images[1]._width, INFO_BOTTOM);
+ s.hLine(images[0]._width, s.height()- 3, s.width() - images[1]._width, INFO_TOP);
+ s.hLine(images[0]._width, s.height()- 2, s.width() - images[1]._width, INFO_MIDDLE);
+ s.hLine(images[0]._width, s.height()- 1, s.width() - images[1]._width, INFO_BOTTOM);
// Draw the left Side of the Info Box
- s.vLine(0, images[0]._height, s.h()- images[2]._height, INFO_TOP);
- s.vLine(1, images[0]._height, s.h()- images[2]._height, INFO_MIDDLE);
- s.vLine(2, images[0]._height, s.h()- images[2]._height, INFO_BOTTOM);
+ s.vLine(0, images[0]._height, s.height()- images[2]._height, INFO_TOP);
+ s.vLine(1, images[0]._height, s.height()- images[2]._height, INFO_MIDDLE);
+ s.vLine(2, images[0]._height, s.height()- images[2]._height, INFO_BOTTOM);
// Draw the right Side of the Info Box
- s.vLine(s.w() - 3, images[0]._height, s.h()- images[2]._height, INFO_TOP);
- s.vLine(s.w() - 2, images[0]._height, s.h()- images[2]._height, INFO_MIDDLE);
- s.vLine(s.w() - 1, images[0]._height, s.h()- images[2]._height, INFO_BOTTOM);
+ s.vLine(s.width() - 3, images[0]._height, s.height()- images[2]._height, INFO_TOP);
+ s.vLine(s.width() - 2, images[0]._height, s.height()- images[2]._height, INFO_MIDDLE);
+ s.vLine(s.width() - 1, images[0]._height, s.height()- images[2]._height, INFO_BOTTOM);
}
void WidgetBase::makeInfoArea() {
diff --git a/engines/sherlock/tattoo/widget_credits.cpp b/engines/sherlock/tattoo/widget_credits.cpp
index b8e297709f..1c878daaf6 100644
--- a/engines/sherlock/tattoo/widget_credits.cpp
+++ b/engines/sherlock/tattoo/widget_credits.cpp
@@ -37,7 +37,7 @@ void WidgetCredits::initCredits() {
Screen &screen = *_vm->_screen;
Common::SeekableReadStream *stream = res.load("credits.txt");
int spacing = screen.fontHeight() * 2;
- int yp = screen.h();
+ int yp = screen.height();
_creditsActive = true;
_creditLines.clear();
@@ -60,7 +60,7 @@ void WidgetCredits::initCredits() {
} else {
int width = screen.stringWidth(line) + 2;
- _creditLines.push_back(CreditLine(line, Common::Point((screen.w() - width) / 2 + 1, yp), width));
+ _creditLines.push_back(CreditLine(line, Common::Point((screen.width() - width) / 2 + 1, yp), width));
yp += spacing;
}
}
@@ -120,10 +120,10 @@ void WidgetCredits::close() {
void WidgetCredits::drawCredits() {
Screen &screen = *_vm->_screen;
- Common::Rect screenRect(0, 0, screen.w(), screen.h());
+ Common::Rect screenRect(0, 0, screen.width(), screen.height());
Surface &bb1 = screen._backBuffer1;
- for (uint idx = 0; idx < _creditLines.size() && _creditLines[idx]._position.y < screen.h(); ++idx) {
+ for (uint idx = 0; idx < _creditLines.size() && _creditLines[idx]._position.y < screen.height(); ++idx) {
if (screenRect.contains(_creditLines[idx]._position)) {
if (!_creditLines[idx]._line2.empty()) {
int x1 = _creditLines[idx]._position.x;
@@ -176,7 +176,7 @@ void WidgetCredits::drawCredits() {
void WidgetCredits::blitCredits() {
Screen &screen = *_vm->_screen;
- Common::Rect screenRect(0, -_creditSpeed, screen.w(), screen.h() + _creditSpeed);
+ Common::Rect screenRect(0, -_creditSpeed, screen.width(), screen.height() + _creditSpeed);
for (uint idx = 0; idx < _creditLines.size(); ++idx) {
if (screenRect.contains(_creditLines[idx]._position)) {
@@ -185,14 +185,12 @@ void WidgetCredits::blitCredits() {
screen.slamRect(r);
}
-
- _creditLines[idx]._position.y -= _creditSpeed;
}
}
void WidgetCredits::eraseCredits() {
Screen &screen = *_vm->_screen;
- Common::Rect screenRect(0, -_creditSpeed, screen.w(), screen.h() + _creditSpeed);
+ Common::Rect screenRect(0, -_creditSpeed, screen.width(), screen.height() + _creditSpeed);
for (uint idx = 0; idx < _creditLines.size(); ++idx) {
if (screenRect.contains(_creditLines[idx]._position)) {
@@ -200,7 +198,10 @@ void WidgetCredits::eraseCredits() {
r.moveTo(_creditLines[idx]._position.x, _creditLines[idx]._position.y - 1 + _creditSpeed);
screen.restoreBackground(r);
+ screen.slamRect(r);
}
+
+ _creditLines[idx]._position.y -= _creditSpeed;
}
if (_creditLines[_creditLines.size() - 1]._position.y < -_creditSpeed) {
diff --git a/engines/sherlock/tattoo/widget_files.cpp b/engines/sherlock/tattoo/widget_files.cpp
index ff8cb83dca..7666e81480 100644
--- a/engines/sherlock/tattoo/widget_files.cpp
+++ b/engines/sherlock/tattoo/widget_files.cpp
@@ -107,36 +107,36 @@ void WidgetFiles::render(FilesRenderMode mode) {
byte color;
if (mode == RENDER_ALL) {
- _surface.fill(TRANSPARENCY);
+ _surface.clear(TRANSPARENCY);
makeInfoArea();
switch (_fileMode) {
case SAVEMODE_LOAD:
_surface.writeString(FIXED(LoadGame),
- Common::Point((_surface.w() - _surface.stringWidth(FIXED(LoadGame))) / 2, 5), INFO_TOP);
+ Common::Point((_surface.width() - _surface.stringWidth(FIXED(LoadGame))) / 2, 5), INFO_TOP);
break;
case SAVEMODE_SAVE:
_surface.writeString(FIXED(SaveGame),
- Common::Point((_surface.w() - _surface.stringWidth(FIXED(SaveGame))) / 2, 5), INFO_TOP);
+ Common::Point((_surface.width() - _surface.stringWidth(FIXED(SaveGame))) / 2, 5), INFO_TOP);
break;
default:
break;
}
- _surface.hLine(3, _surface.fontHeight() + 7, _surface.w() - 4, INFO_TOP);
- _surface.hLine(3, _surface.fontHeight() + 8, _surface.w() - 4, INFO_MIDDLE);
- _surface.hLine(3, _surface.fontHeight() + 9, _surface.w() - 4, INFO_BOTTOM);
- _surface.transBlitFrom(images[4], Common::Point(0, _surface.fontHeight() + 6));
- _surface.transBlitFrom(images[5], Common::Point(_surface.w() - images[5]._width, _surface.fontHeight() + 6));
+ _surface.hLine(3, _surface.fontHeight() + 7, _surface.width() - 4, INFO_TOP);
+ _surface.hLine(3, _surface.fontHeight() + 8, _surface.width() - 4, INFO_MIDDLE);
+ _surface.hLine(3, _surface.fontHeight() + 9, _surface.width() - 4, INFO_BOTTOM);
+ _surface.SHtransBlitFrom(images[4], Common::Point(0, _surface.fontHeight() + 6));
+ _surface.SHtransBlitFrom(images[5], Common::Point(_surface.width() - images[5]._width, _surface.fontHeight() + 6));
- int xp = _surface.w() - BUTTON_SIZE - 6;
+ int xp = _surface.width() - BUTTON_SIZE - 6;
_surface.vLine(xp, _surface.fontHeight() + 10, _bounds.height() - 4, INFO_TOP);
_surface.vLine(xp + 1, _surface.fontHeight() + 10, _bounds.height() - 4, INFO_MIDDLE);
_surface.vLine(xp + 2, _surface.fontHeight() + 10, _bounds.height() - 4, INFO_BOTTOM);
- _surface.transBlitFrom(images[6], Common::Point(xp - 1, _surface.fontHeight() + 8));
- _surface.transBlitFrom(images[7], Common::Point(xp - 1, _bounds.height() - 4));
+ _surface.SHtransBlitFrom(images[6], Common::Point(xp - 1, _surface.fontHeight() + 8));
+ _surface.SHtransBlitFrom(images[7], Common::Point(xp - 1, _bounds.height() - 4));
}
int xp = _surface.stringWidth("00.") + _surface.widestChar() + 5;
@@ -149,7 +149,7 @@ void WidgetFiles::render(FilesRenderMode mode) {
color = INFO_TOP;
if (mode == RENDER_NAMES_AND_SCROLLBAR)
- _surface.fillRect(Common::Rect(4, yp, _surface.w() - BUTTON_SIZE - 9, yp + _surface.fontHeight()), TRANSPARENCY);
+ _surface.fillRect(Common::Rect(4, yp, _surface.width() - BUTTON_SIZE - 9, yp + _surface.fontHeight()), TRANSPARENCY);
Common::String numStr = Common::String::format("%d.", idx + 1);
_surface.writeString(numStr, Common::Point(_surface.widestChar(), yp), color);
@@ -324,7 +324,7 @@ bool WidgetFiles::getFilename() {
filename.setChar(' ', index);
}
- _surface.fillRect(Common::Rect(pt.x, pt.y, _surface.w() - BUTTON_SIZE - 9, pt.y + _surface.fontHeight() - 1), TRANSPARENCY);
+ _surface.fillRect(Common::Rect(pt.x, pt.y, _surface.width() - BUTTON_SIZE - 9, pt.y + _surface.fontHeight() - 1), TRANSPARENCY);
_surface.writeString(filename.c_str() + index, pt, COMMAND_HIGHLIGHTED);
} else if ((keyState.keycode == Common::KEYCODE_LEFT && index > 0)
@@ -387,7 +387,7 @@ bool WidgetFiles::getFilename() {
}
if ((keyState.ascii >= ' ') && (keyState.ascii <= 'z') && (index < 50)) {
- if (pt.x + _surface.charWidth(keyState.ascii) < _surface.w() - BUTTON_SIZE - 20) {
+ if (pt.x + _surface.charWidth(keyState.ascii) < _surface.w - BUTTON_SIZE - 20) {
if (insert)
filename.insertChar(keyState.ascii, index);
else
diff --git a/engines/sherlock/tattoo/widget_foolscap.cpp b/engines/sherlock/tattoo/widget_foolscap.cpp
index c8df71e873..8225946838 100644
--- a/engines/sherlock/tattoo/widget_foolscap.cpp
+++ b/engines/sherlock/tattoo/widget_foolscap.cpp
@@ -103,7 +103,7 @@ void WidgetFoolscap::show() {
// Set up the window background
_surface.create(_bounds.width(), _bounds.height());
- _surface.blitFrom(paperFrame, Common::Point(0, 0));
+ _surface.SHblitFrom(paperFrame, Common::Point(0, 0));
// If they have already solved the puzzle, put the answer on the graphic
if (_vm->readFlags(299)) {
@@ -265,7 +265,7 @@ void WidgetFoolscap::handleKeyboardEvents() {
void WidgetFoolscap::restoreChar() {
Screen &screen = *_vm->_screen;
ImageFrame &bgFrame = (*_images)[0];
- _surface.blitFrom(bgFrame, _cursorPos, Common::Rect(_cursorPos.x, _cursorPos.y,
+ _surface.SHblitFrom(bgFrame, _cursorPos, Common::Rect(_cursorPos.x, _cursorPos.y,
_cursorPos.x + screen.widestChar(), _cursorPos.y + screen.fontHeight()));
}
diff --git a/engines/sherlock/tattoo/widget_inventory.cpp b/engines/sherlock/tattoo/widget_inventory.cpp
index b49e30b30d..34331f0eae 100644
--- a/engines/sherlock/tattoo/widget_inventory.cpp
+++ b/engines/sherlock/tattoo/widget_inventory.cpp
@@ -94,7 +94,7 @@ void WidgetInventoryTooltip::setText(const Common::String &str) {
// Allocate a fresh surface for the new string
_bounds = Common::Rect(width, height);
_surface.create(width, height);
- _surface.fill(TRANSPARENCY);
+ _surface.clear(TRANSPARENCY);
if (line2.empty()) {
_surface.writeFancyString(str, Common::Point(0, 0), BLACK, INFO_TOP);
@@ -338,7 +338,7 @@ void WidgetInventoryVerbs::load() {
// Create the surface
_surface.create(_bounds.width(), _bounds.height());
- _surface.fill(TRANSPARENCY);
+ _surface.clear(TRANSPARENCY);
makeInfoArea();
// Draw the Verb commands and the lines separating them
@@ -352,8 +352,8 @@ void WidgetInventoryVerbs::load() {
_surface.vLine(3, (_surface.fontHeight() + 7) * (idx + 1) + 1, _bounds.right - 4, INFO_MIDDLE);
_surface.vLine(3, (_surface.fontHeight() + 7) * (idx + 1) + 2, _bounds.right - 4, INFO_BOTTOM);
- _surface.transBlitFrom(images[4], Common::Point(0, (_surface.fontHeight() + 7) * (idx + 1)));
- _surface.transBlitFrom(images[5], Common::Point(_bounds.width() - images[5]._width,
+ _surface.SHtransBlitFrom(images[4], Common::Point(0, (_surface.fontHeight() + 7) * (idx + 1)));
+ _surface.SHtransBlitFrom(images[5], Common::Point(_bounds.width() - images[5]._width,
(_surface.fontHeight() + 7) * (idx + 1) - 1));
}
}
@@ -515,7 +515,7 @@ void WidgetInventory::load(int mode) {
// Redraw the inventory menu on the widget surface
_surface.create(_bounds.width(), _bounds.height());
- _surface.fill(TRANSPARENCY);
+ _surface.clear(TRANSPARENCY);
// Draw the window background and then the inventory on top of it
makeInfoArea(_surface);
@@ -531,7 +531,7 @@ void WidgetInventory::drawBars() {
_surface.hLine(3, INVENTORY_YSIZE + 3, _bounds.width() - 4, INFO_TOP);
_surface.hLine(3, INVENTORY_YSIZE + 4, _bounds.width() - 4, INFO_MIDDLE);
_surface.hLine(3, INVENTORY_YSIZE + 5, _bounds.width() - 4, INFO_BOTTOM);
- _surface.transBlitFrom(images[4], Common::Point(0, INVENTORY_YSIZE + 2));
+ _surface.SHtransBlitFrom(images[4], Common::Point(0, INVENTORY_YSIZE + 2));
for (int idx = 1; idx <= NUM_INVENTORY_SHOWN / 2; ++idx) {
x = idx * (INVENTORY_XSIZE + 3);
@@ -540,10 +540,10 @@ void WidgetInventory::drawBars() {
_surface.vLine(x + 1, 3, _bounds.height() - 4, INFO_MIDDLE);
_surface.vLine(x + 2, 3, _bounds.height() - 4, INFO_BOTTOM);
- _surface.transBlitFrom(images[6], Common::Point(x - 1, 1));
- _surface.transBlitFrom(images[7], Common::Point(x - 1, _bounds.height() - 4));
- _surface.transBlitFrom(images[6], Common::Point(x - 1, INVENTORY_YSIZE + 5));
- _surface.transBlitFrom(images[7], Common::Point(x - 1, INVENTORY_YSIZE + 2));
+ _surface.SHtransBlitFrom(images[6], Common::Point(x - 1, 1));
+ _surface.SHtransBlitFrom(images[7], Common::Point(x - 1, _bounds.height() - 4));
+ _surface.SHtransBlitFrom(images[6], Common::Point(x - 1, INVENTORY_YSIZE + 5));
+ _surface.SHtransBlitFrom(images[7], Common::Point(x - 1, INVENTORY_YSIZE + 2));
}
_surface.hLine(x + 2, INVENTORY_YSIZE + 2, INVENTORY_YSIZE + 8, INFO_BOTTOM);
@@ -566,7 +566,7 @@ void WidgetInventory::drawInventory() {
// Draw the item
if (itemId < inv._holdings) {
ImageFrame &img = (*inv._invShapes[idx])[0];
- _surface.transBlitFrom(img, Common::Point(pt.x + (INVENTORY_XSIZE - img._width) / 2,
+ _surface.SHtransBlitFrom(img, Common::Point(pt.x + (INVENTORY_XSIZE - img._width) / 2,
pt.y + (INVENTORY_YSIZE - img._height) / 2));
}
}
diff --git a/engines/sherlock/tattoo/widget_options.cpp b/engines/sherlock/tattoo/widget_options.cpp
index 92bd10bbf6..81f50f3bc5 100644
--- a/engines/sherlock/tattoo/widget_options.cpp
+++ b/engines/sherlock/tattoo/widget_options.cpp
@@ -257,17 +257,17 @@ void WidgetOptions::render(OptionRenderMode mode) {
// Setup the dialog
_surface.create(_bounds.width(), _bounds.height());
- _surface.fill(TRANSPARENCY);
+ _surface.clear(TRANSPARENCY);
makeInfoArea();
// Draw the lines separating options in the dialog
int yp = _surface.fontHeight() + 7;
for (int idx = 0; idx < 7; ++idx) {
- _surface.transBlitFrom(images[4], Common::Point(0, yp - 1));
- _surface.transBlitFrom(images[5], Common::Point(_surface.w() - images[5]._width, yp - 1));
- _surface.hLine(3, yp, _surface.w() - 4, INFO_TOP);
- _surface.hLine(3, yp + 1, _surface.w() - 4, INFO_MIDDLE);
- _surface.hLine(3, yp + 2, _surface.w() - 4, INFO_BOTTOM);
+ _surface.SHtransBlitFrom(images[4], Common::Point(0, yp - 1));
+ _surface.SHtransBlitFrom(images[5], Common::Point(_surface.width() - images[5]._width, yp - 1));
+ _surface.hLine(3, yp, _surface.width() - 4, INFO_TOP);
+ _surface.hLine(3, yp + 1, _surface.width() - 4, INFO_MIDDLE);
+ _surface.hLine(3, yp + 2, _surface.width() - 4, INFO_BOTTOM);
yp += _surface.fontHeight() + 7;
if (idx == 1)
@@ -281,7 +281,7 @@ void WidgetOptions::render(OptionRenderMode mode) {
for (int idx = 0, yp = 5; idx < 11; ++idx, yp += _surface.fontHeight() + 7) {
if (mode == OP_ALL || idx == _selector || idx == _oldSelector) {
if (mode == OP_NAMES)
- _surface.fillRect(Common::Rect(4, yp, _surface.w() - 5, yp + _surface.fontHeight() - 1), TRANSPARENCY);
+ _surface.fillRect(Common::Rect(4, yp, _surface.width() - 5, yp + _surface.fontHeight() - 1), TRANSPARENCY);
byte color = (idx == _selector) ? COMMAND_HIGHLIGHTED : INFO_TOP;
Common::String str;
@@ -302,11 +302,11 @@ void WidgetOptions::render(OptionRenderMode mode) {
int num = (_surface.fontHeight() + 4) & 0xfe;
int sliderY = yp + num / 2 - 8;
- _surface.fillRect(Common::Rect(4, sliderY - (num - 6) / 2, _surface.w() - 5,
+ _surface.fillRect(Common::Rect(4, sliderY - (num - 6) / 2, _surface.width() - 5,
sliderY - (num - 6) / 2 + num - 1), TRANSPARENCY);
_surface.fillRect(Common::Rect(_surface.widestChar(), sliderY + 2,
- _surface.w() - _surface.widestChar() - 1, sliderY + 3), INFO_MIDDLE);
- drawDialogRect(Common::Rect(_surface.widestChar(), sliderY, _surface.w() - _surface.widestChar(), sliderY + 6));
+ _surface.width() - _surface.widestChar() - 1, sliderY + 3), INFO_MIDDLE);
+ drawDialogRect(Common::Rect(_surface.widestChar(), sliderY, _surface.width() - _surface.widestChar(), sliderY + 6));
_surface.fillRect(Common::Rect(_midiSliderX - 1, sliderY - (num - 6) / 2 + 2,
_midiSliderX + 1, sliderY - (num - 6) / 2 + num - 3), INFO_MIDDLE);
@@ -315,7 +315,7 @@ void WidgetOptions::render(OptionRenderMode mode) {
if (_midiSliderX - 4 > _surface.widestChar())
_surface.fillRect(Common::Rect(_midiSliderX - 4, sliderY, _midiSliderX - 4, sliderY + 4), INFO_BOTTOM);
- if (_midiSliderX + 4 < _surface.w() - _surface.widestChar())
+ if (_midiSliderX + 4 < _surface.width() - _surface.widestChar())
_surface.fillRect(Common::Rect(_midiSliderX + 4, sliderY, _midiSliderX + 4, sliderY + 4), INFO_BOTTOM);
break;
}
@@ -332,18 +332,18 @@ void WidgetOptions::render(OptionRenderMode mode) {
int num = (_surface.fontHeight() + 4) & 0xfe;
int sliderY = yp + num / 2 - 8;
- _surface.fillRect(Common::Rect(4, sliderY - (num - 6) / 2, _surface.w() - 5,
+ _surface.fillRect(Common::Rect(4, sliderY - (num - 6) / 2, _surface.width() - 5,
sliderY - (num - 6) / 2 + num - 1), TRANSPARENCY);
- _surface.fillRect(Common::Rect(_surface.widestChar(), sliderY + 2, _surface.w() - _surface.widestChar() - 1,
+ _surface.fillRect(Common::Rect(_surface.widestChar(), sliderY + 2, _surface.width() - _surface.widestChar() - 1,
sliderY + 3), INFO_MIDDLE);
- drawDialogRect(Common::Rect(_surface.widestChar(), sliderY, _surface.w() - _surface.widestChar(), sliderY + 6));
+ drawDialogRect(Common::Rect(_surface.widestChar(), sliderY, _surface.width() - _surface.widestChar(), sliderY + 6));
_surface.fillRect(Common::Rect(_digiSliderX - 1, sliderY - (num - 6) / 2 + 2, _digiSliderX + 1,
sliderY - (num - 6) / 2 + num - 3), INFO_MIDDLE);
drawDialogRect(Common::Rect(_digiSliderX - 3, sliderY - (num - 6) / 2, _digiSliderX + 4,
sliderY - (num - 6) / 2 + num));
if (_digiSliderX - 4 > _surface.widestChar())
_surface.fillRect(Common::Rect(_digiSliderX - 4, sliderY, _digiSliderX - 4, sliderY + 4), INFO_BOTTOM);
- if (_digiSliderX + 4 < _surface.w() - _surface.widestChar())
+ if (_digiSliderX + 4 < _surface.width() - _surface.widestChar())
_surface.fillRect(Common::Rect(_digiSliderX + 4, sliderY, _digiSliderX + 4, sliderY + 4), INFO_BOTTOM);
break;
}
@@ -375,7 +375,7 @@ void WidgetOptions::render(OptionRenderMode mode) {
// Unless we're doing one of the Slider Controls, print the text for the line
if (idx != 3 && idx != 6) {
- int xp = (_surface.w() - _surface.stringWidth(str)) / 2;
+ int xp = (_surface.width() - _surface.stringWidth(str)) / 2;
_surface.writeString(str, Common::Point(xp, yp), color);
}
}
diff --git a/engines/sherlock/tattoo/widget_password.cpp b/engines/sherlock/tattoo/widget_password.cpp
index 57a5e02653..2a2921026d 100644
--- a/engines/sherlock/tattoo/widget_password.cpp
+++ b/engines/sherlock/tattoo/widget_password.cpp
@@ -47,7 +47,7 @@ void WidgetPassword::show() {
// Create the surface
_surface.create(_bounds.width(), _bounds.height());
- _surface.fill(TRANSPARENCY);
+ _surface.clear(TRANSPARENCY);
makeInfoArea();
// Draw the header area
@@ -55,8 +55,8 @@ void WidgetPassword::show() {
_surface.hLine(3, _surface.fontHeight() + 7, _bounds.width() - 4, INFO_TOP);
_surface.hLine(3, _surface.fontHeight() + 8, _bounds.width() - 4, INFO_MIDDLE);
_surface.hLine(3, _surface.fontHeight() + 9, _bounds.width() - 4, INFO_BOTTOM);
- _surface.transBlitFrom(images[4], Common::Point(0, _surface.fontHeight() + 7 - 1));
- _surface.transBlitFrom(images[5], Common::Point(_bounds.width() - images[5]._width, _surface.fontHeight() + 7 - 1));
+ _surface.SHtransBlitFrom(images[4], Common::Point(0, _surface.fontHeight() + 7 - 1));
+ _surface.SHtransBlitFrom(images[5], Common::Point(_bounds.width() - images[5]._width, _surface.fontHeight() + 7 - 1));
// Set the password entry data
_cursorPos = Common::Point(_surface.widestChar(), _surface.fontHeight() + 12);
diff --git a/engines/sherlock/tattoo/widget_quit.cpp b/engines/sherlock/tattoo/widget_quit.cpp
index f853e7f47f..ea8f2e080c 100644
--- a/engines/sherlock/tattoo/widget_quit.cpp
+++ b/engines/sherlock/tattoo/widget_quit.cpp
@@ -48,22 +48,22 @@ void WidgetQuit::show() {
// Create the surface
_surface.create(_bounds.width(), _bounds.height());
- _surface.fill(TRANSPARENCY);
+ _surface.clear(TRANSPARENCY);
makeInfoArea();
// Draw the message text
- _surface.writeString(FIXED(AreYouSureYou), Common::Point((_surface.w() - _surface.stringWidth(FIXED(AreYouSureYou))) / 2, 5), INFO_TOP);
- _surface.writeString(FIXED(WishToQuit), Common::Point((_surface.w() - _surface.stringWidth(FIXED(WishToQuit))) / 2,
+ _surface.writeString(FIXED(AreYouSureYou), Common::Point((_surface.width() - _surface.stringWidth(FIXED(AreYouSureYou))) / 2, 5), INFO_TOP);
+ _surface.writeString(FIXED(WishToQuit), Common::Point((_surface.width() - _surface.stringWidth(FIXED(WishToQuit))) / 2,
_surface.fontHeight() + 9), INFO_TOP);
// Draw the horizontal bars seperating the commands and the message
int yp = (_surface.fontHeight() + 4) * 2 + 3;
for (int idx = 0; idx < 2; ++idx) {
- _surface.transBlitFrom(images[4], Common::Point(0, yp - 1));
- _surface.transBlitFrom(images[5], Common::Point(_surface.w() - images[5]._width, yp - 1));
- _surface.hLine(3, yp, _surface.w() - 4, INFO_TOP);
- _surface.hLine(3, yp + 1, _surface.w() - 4, INFO_MIDDLE);
- _surface.hLine(3, yp + 2, _surface.w() - 4, INFO_BOTTOM);
+ _surface.SHtransBlitFrom(images[4], Common::Point(0, yp - 1));
+ _surface.SHtransBlitFrom(images[5], Common::Point(_surface.width() - images[5]._width, yp - 1));
+ _surface.hLine(3, yp, _surface.width() - 4, INFO_TOP);
+ _surface.hLine(3, yp + 1, _surface.width() - 4, INFO_MIDDLE);
+ _surface.hLine(3, yp + 2, _surface.width() - 4, INFO_BOTTOM);
const char *btn = (idx == 0) ? YES : NO;
_surface.writeString(btn, Common::Point((_bounds.width() - _surface.stringWidth(btn)) / 2, yp + 5), INFO_TOP);
@@ -129,11 +129,11 @@ void WidgetQuit::handleEvents() {
if (_select != _oldSelect) {
byte color = (_select == 1) ? COMMAND_HIGHLIGHTED : INFO_TOP;
int yp = (_surface.fontHeight() + 4) * 2 + 8;
- _surface.writeString(FIXED(Yes), Common::Point((_surface.w() - _surface.stringWidth(FIXED(Yes))) / 2, yp), color);
+ _surface.writeString(FIXED(Yes), Common::Point((_surface.width() - _surface.stringWidth(FIXED(Yes))) / 2, yp), color);
color = (_select == 0) ? COMMAND_HIGHLIGHTED : INFO_TOP;
yp += (_surface.fontHeight() + 7);
- _surface.writeString(FIXED(No), Common::Point((_surface.w() - _surface.stringWidth(FIXED(No))) / 2, yp), color);
+ _surface.writeString(FIXED(No), Common::Point((_surface.width() - _surface.stringWidth(FIXED(No))) / 2, yp), color);
}
_oldSelect = _select;
diff --git a/engines/sherlock/tattoo/widget_talk.cpp b/engines/sherlock/tattoo/widget_talk.cpp
index 6e7bde292f..b673f32d31 100644
--- a/engines/sherlock/tattoo/widget_talk.cpp
+++ b/engines/sherlock/tattoo/widget_talk.cpp
@@ -100,7 +100,7 @@ void WidgetTalk::load() {
// Set up the surface
_surface.create(_bounds.width(), _bounds.height());
- _surface.fill(TRANSPARENCY);
+ _surface.clear(TRANSPARENCY);
// Form the background for the new window
makeInfoArea();
@@ -389,7 +389,7 @@ void WidgetTalk::render(Highlight highlightMode) {
if (highlightMode == HL_NO_HIGHLIGHTING || _statementLines[idx]._num == _selector ||
_statementLines[idx]._num == _oldSelector) {
// Erase the line contents
- _surface.fillRect(Common::Rect(3, yp, _surface.w() - BUTTON_SIZE - 3, yp + _surface.fontHeight()), TRANSPARENCY);
+ _surface.fillRect(Common::Rect(3, yp, _surface.width() - BUTTON_SIZE - 3, yp + _surface.fontHeight()), TRANSPARENCY);
// Different coloring based on whether the option has been previously chosen or not
byte color = (!talk._talkHistory[talk._converseNum][_statementLines[idx]._num]) ?
diff --git a/engines/sherlock/tattoo/widget_text.cpp b/engines/sherlock/tattoo/widget_text.cpp
index 86aa067301..a29cd2700f 100644
--- a/engines/sherlock/tattoo/widget_text.cpp
+++ b/engines/sherlock/tattoo/widget_text.cpp
@@ -80,6 +80,7 @@ void WidgetText::centerWindowOnSpeaker(int speaker) {
TattooScene &scene = *(TattooScene *)_vm->_scene;
Common::Point pt;
+ speaker &= 0x7f;
bool flag = _vm->readFlags(FLAG_PLAYER_IS_HOLMES);
if (people[HOLMES]._type == CHARACTER && ((speaker == HOLMES && flag) || (speaker == WATSON && !flag))) {
// Place the window centered above the player
@@ -165,7 +166,7 @@ void WidgetText::render(const Common::String &str) {
// Allocate a surface for the window
_surface.create(_bounds.width(), _bounds.height());
- _surface.fill(TRANSPARENCY);
+ _surface.clear(TRANSPARENCY);
// Form the background for the new window
makeInfoArea();
@@ -194,7 +195,7 @@ void WidgetMessage::load(const Common::String &str, int time) {
// Allocate a surface for the window
_surface.create(_bounds.width(), _bounds.height());
- _surface.fill(TRANSPARENCY);
+ _surface.clear(TRANSPARENCY);
// Form the background for the new window and write the line of text
makeInfoArea();
diff --git a/engines/sherlock/tattoo/widget_tooltip.cpp b/engines/sherlock/tattoo/widget_tooltip.cpp
index b29f45f531..1560cb9a80 100644
--- a/engines/sherlock/tattoo/widget_tooltip.cpp
+++ b/engines/sherlock/tattoo/widget_tooltip.cpp
@@ -47,7 +47,7 @@ void WidgetTooltipBase::draw() {
// Draw the widget directly onto the screen. Unlike other widgets, we don't draw to the back buffer,
// since nothing should be drawing on top of tooltips, so there's no need to store in the back buffer
- screen.transBlitFrom(_surface, Common::Point(_bounds.left - screen._currentScroll.x,
+ screen.SHtransBlitFrom(_surface, Common::Point(_bounds.left - screen._currentScroll.x,
_bounds.top - screen._currentScroll.y));
// Store a copy of the drawn area for later erasing
@@ -126,7 +126,7 @@ void WidgetTooltip::setText(const Common::String &str) {
// Reallocate the text surface with the new size
_surface.create(width, height);
- _surface.fill(TRANSPARENCY);
+ _surface.clear(TRANSPARENCY);
if (line2.empty()) {
// Only a single line
diff --git a/engines/sherlock/tattoo/widget_verbs.cpp b/engines/sherlock/tattoo/widget_verbs.cpp
index 499afb2e79..5041888ffb 100644
--- a/engines/sherlock/tattoo/widget_verbs.cpp
+++ b/engines/sherlock/tattoo/widget_verbs.cpp
@@ -127,7 +127,7 @@ void WidgetVerbs::render() {
// Create the drawing surface
_surface.create(_bounds.width(), _bounds.height());
- _surface.fill(TRANSPARENCY);
+ _surface.clear(TRANSPARENCY);
// Draw basic background
makeInfoArea();
@@ -142,8 +142,8 @@ void WidgetVerbs::render() {
_surface.hLine(3, (_surface.fontHeight() + 7) * (idx + 1) + 1, _bounds.width() - 4, INFO_MIDDLE);
_surface.hLine(3, (_surface.fontHeight() + 7) * (idx + 1) + 2, _bounds.width() - 4, INFO_BOTTOM);
- _surface.transBlitFrom(images[4], Common::Point(0, (_surface.fontHeight() + 7) * (idx + 1) - 1));
- _surface.transBlitFrom(images[5], Common::Point(_bounds.width() - images[5]._width,
+ _surface.SHtransBlitFrom(images[4], Common::Point(0, (_surface.fontHeight() + 7) * (idx + 1) - 1));
+ _surface.SHtransBlitFrom(images[5], Common::Point(_bounds.width() - images[5]._width,
(_surface.fontHeight() + 7) * (idx + 1) - 1));
}
}
diff --git a/engines/sky/detection.cpp b/engines/sky/detection.cpp
index 8a8967629d..4b91f50a61 100644
--- a/engines/sky/detection.cpp
+++ b/engines/sky/detection.cpp
@@ -136,7 +136,7 @@ const ExtraGuiOptions SkyMetaEngine::getExtraGuiOptions(const Common::String &ta
}
GameDescriptor SkyMetaEngine::findGame(const char *gameid) const {
- if (0 == scumm_stricmp(gameid, skySetting.gameid))
+ if (0 == scumm_stricmp(gameid, skySetting.gameId))
return skySetting;
return GameDescriptor();
}
@@ -175,7 +175,7 @@ GameList SkyMetaEngine::detectGames(const Common::FSList &fslist) const {
// Match found, add to list of candidates, then abort inner loop.
// The game detector uses US English by default. We want British
// English to match the recorded voices better.
- GameDescriptor dg(skySetting.gameid, skySetting.description, Common::UNK_LANG, Common::kPlatformUnknown);
+ GameDescriptor dg(skySetting.gameId, skySetting.description, Common::UNK_LANG, Common::kPlatformUnknown);
const SkyVersion *sv = skyVersions;
while (sv->dinnerTableEntries) {
if (dinnerTableEntries == sv->dinnerTableEntries &&
@@ -223,7 +223,6 @@ SaveStateList SkyMetaEngine::listSaves(const char *target) const {
// Find all saves
Common::StringArray filenames;
filenames = saveFileMan->listSavefiles("SKY-VM.###");
- sort(filenames.begin(), filenames.end()); // Sort (hopefully ensuring we are sorted numerically..)
// Slot 0 is the autosave, if it exists.
// TODO: Check for the existence of the autosave -- but this require us
@@ -243,6 +242,8 @@ SaveStateList SkyMetaEngine::listSaves(const char *target) const {
}
}
+ // Sort saves based on slot number.
+ Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator());
return saveList;
}
diff --git a/engines/sword1/configure.engine b/engines/sword1/configure.engine
index 0578d176a9..1d17903b69 100644
--- a/engines/sword1/configure.engine
+++ b/engines/sword1/configure.engine
@@ -1,3 +1,3 @@
# This file is included from the main "configure" script
# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps]
-add_engine sword1 "Broken Sword" yes
+add_engine sword1 "Broken Sword" yes "" "" "highres"
diff --git a/engines/sword1/detection.cpp b/engines/sword1/detection.cpp
index 99c4bda9e9..0edf856125 100644
--- a/engines/sword1/detection.cpp
+++ b/engines/sword1/detection.cpp
@@ -128,17 +128,17 @@ GameList SwordMetaEngine::getSupportedGames() const {
}
GameDescriptor SwordMetaEngine::findGame(const char *gameid) const {
- if (0 == scumm_stricmp(gameid, sword1FullSettings.gameid))
+ if (0 == scumm_stricmp(gameid, sword1FullSettings.gameId))
return sword1FullSettings;
- if (0 == scumm_stricmp(gameid, sword1DemoSettings.gameid))
+ if (0 == scumm_stricmp(gameid, sword1DemoSettings.gameId))
return sword1DemoSettings;
- if (0 == scumm_stricmp(gameid, sword1MacFullSettings.gameid))
+ if (0 == scumm_stricmp(gameid, sword1MacFullSettings.gameId))
return sword1MacFullSettings;
- if (0 == scumm_stricmp(gameid, sword1MacDemoSettings.gameid))
+ if (0 == scumm_stricmp(gameid, sword1MacDemoSettings.gameId))
return sword1MacDemoSettings;
- if (0 == scumm_stricmp(gameid, sword1PSXSettings.gameid))
+ if (0 == scumm_stricmp(gameid, sword1PSXSettings.gameId))
return sword1PSXSettings;
- if (0 == scumm_stricmp(gameid, sword1PSXDemoSettings.gameid))
+ if (0 == scumm_stricmp(gameid, sword1PSXDemoSettings.gameId))
return sword1PSXDemoSettings;
return GameDescriptor();
}
@@ -241,7 +241,6 @@ SaveStateList SwordMetaEngine::listSaves(const char *target) const {
char saveName[40];
Common::StringArray filenames = saveFileMan->listSavefiles("sword1.###");
- sort(filenames.begin(), filenames.end()); // Sort (hopefully ensuring we are sorted numerically..)
int slotNum = 0;
for (Common::StringArray::const_iterator file = filenames.begin(); file != filenames.end(); ++file) {
@@ -259,6 +258,8 @@ SaveStateList SwordMetaEngine::listSaves(const char *target) const {
}
}
+ // Sort saves based on slot number.
+ Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator());
return saveList;
}
diff --git a/engines/sword1/objectman.cpp b/engines/sword1/objectman.cpp
index 07f19154a0..651e47f211 100644
--- a/engines/sword1/objectman.cpp
+++ b/engines/sword1/objectman.cpp
@@ -119,7 +119,7 @@ char *ObjectMan::lockText(uint32 textId, uint8 lang) {
return NULL;
addr += sizeof(Header);
if ((textId & ITM_ID) >= _resMan->readUint32(addr)) {
- // Workaround for missing sentences in some langages in the demo.
+ // Workaround for missing sentences in some languages in the demo.
switch(textId) {
case 8455194:
return const_cast<char *>(_translationId8455194[lang]);
@@ -160,7 +160,7 @@ char *ObjectMan::lockText(uint32 textId, uint8 lang) {
}
uint32 offset = _resMan->readUint32(addr + ((textId & ITM_ID) + 1) * 4);
if (offset == 0) {
- // Workaround bug for missing sentence in some langages in Syria (see bug #1977094).
+ // Workaround bug for missing sentence in some languages in Syria (see bug #1977094).
// We use the hardcoded text in this case.
if (textId == 2950145)
return const_cast<char *>(_translationId2950145[lang]);
@@ -223,7 +223,7 @@ void ObjectMan::saveLiveList(uint16 *dest) {
}
// String displayed when a subtitle sentence is missing in the cluster file.
-// It happens with at least one sentence in Syria in some langages (see bug
+// It happens with at least one sentence in Syria in some languages (see bug
// #1977094).
// Note: an empty string or a null pointer causes a crash.
diff --git a/engines/sword1/objectman.h b/engines/sword1/objectman.h
index fef1a8da3a..79be82c02f 100644
--- a/engines/sword1/objectman.h
+++ b/engines/sword1/objectman.h
@@ -62,7 +62,7 @@ private:
uint16 _liveList[TOTAL_SECTIONS]; //which sections are active
uint8 *_cptData[TOTAL_SECTIONS];
static char _missingSubTitleStr[];
- static const char *const _translationId2950145[7]; //translation for textId 2950145 (missing from cluster file for some langages)
+ static const char *const _translationId2950145[7]; //translation for textId 2950145 (missing from cluster file for some languages)
static const char *const _translationId8455194[7]; //translation for textId 8455194 (missing in the demo)
static const char *const _translationId8455195[7]; //translation for textId 8455195 (missing in the demo)
static const char *const _translationId8455196[7]; //translation for textId 8455196 (missing in the demo)
diff --git a/engines/sword1/router.cpp b/engines/sword1/router.cpp
index 72c8440e1c..0c2e9569b6 100644
--- a/engines/sword1/router.cpp
+++ b/engines/sword1/router.cpp
@@ -1455,7 +1455,7 @@ int32 Router::newCheck(int32 status, int32 x1, int32 y1, int32 x2, int32 y2) {
* newCheck differs from check in that that 4 route options are
* considered corresponding to actual walked routes.
*
- * Note distance doesnt take account of shrinking ???
+ * Note distance doesn't take account of shrinking ???
*
* Note Bars array must be properly calculated ie min max dx dy co
*********************************************************************/
diff --git a/engines/sword2/configure.engine b/engines/sword2/configure.engine
index 7153605433..a794e7287c 100644
--- a/engines/sword2/configure.engine
+++ b/engines/sword2/configure.engine
@@ -1,3 +1,3 @@
# This file is included from the main "configure" script
# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps]
-add_engine sword2 "Broken Sword II" yes
+add_engine sword2 "Broken Sword II" yes "" "" "highres"
diff --git a/engines/sword2/router.cpp b/engines/sword2/router.cpp
index d3f274dd2c..e95fa367a0 100644
--- a/engines/sword2/router.cpp
+++ b/engines/sword2/router.cpp
@@ -1712,7 +1712,7 @@ int32 Router::newCheck(int32 status, int32 x1, int32 y1, int32 x2, int32 y2) {
* newCheck differs from check in that that 4 route options are
* considered corresponding to actual walked routes.
*
- * Note distance doesnt take account of shrinking ???
+ * Note distance doesn't take account of shrinking ???
*
* Note Bars array must be properly calculated ie min max dx dy co
*********************************************************************/
diff --git a/engines/sword2/sword2.cpp b/engines/sword2/sword2.cpp
index baedecb52f..44371bf6cf 100644
--- a/engines/sword2/sword2.cpp
+++ b/engines/sword2/sword2.cpp
@@ -234,7 +234,6 @@ SaveStateList Sword2MetaEngine::listSaves(const char *target) const {
pattern += ".###";
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) {
@@ -252,6 +251,8 @@ SaveStateList Sword2MetaEngine::listSaves(const char *target) const {
}
}
+ // Sort saves based on slot number.
+ Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator());
return saveList;
}
diff --git a/engines/sword25/POTFILES b/engines/sword25/POTFILES
new file mode 100644
index 0000000000..f4b0e6fc27
--- /dev/null
+++ b/engines/sword25/POTFILES
@@ -0,0 +1 @@
+engines/sword25/detection.cpp
diff --git a/engines/sword25/configure.engine b/engines/sword25/configure.engine
index 6a9428c758..f805483f54 100644
--- a/engines/sword25/configure.engine
+++ b/engines/sword25/configure.engine
@@ -1,3 +1,3 @@
# This file is included from the main "configure" script
# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps]
-add_engine sword25 "Broken Sword 2.5" yes "" "" "png zlib 16bit"
+add_engine sword25 "Broken Sword 2.5" yes "" "" "png zlib 16bit highres"
diff --git a/engines/sword25/detection.cpp b/engines/sword25/detection.cpp
index df68d11609..4ca565c972 100644
--- a/engines/sword25/detection.cpp
+++ b/engines/sword25/detection.cpp
@@ -21,6 +21,7 @@
*/
#include "base/plugins.h"
+#include "common/translation.h"
#include "engines/advancedDetector.h"
#include "sword25/sword25.h"
@@ -41,10 +42,17 @@ static const char *directoryGlobs[] = {
0
};
+static const ExtraGuiOption sword25ExtraGuiOption = {
+ _s("Use English speech"),
+ _s("Use English speech instead of German for every language other than German"),
+ "english_speech",
+ false
+};
+
class Sword25MetaEngine : public AdvancedMetaEngine {
public:
Sword25MetaEngine() : AdvancedMetaEngine(Sword25::gameDescriptions, sizeof(ADGameDescription), sword25Game) {
- _guioptions = GUIO1(GUIO_NOMIDI);
+ _guiOptions = GUIO1(GUIO_NOMIDI);
_maxScanDepth = 2;
_directoryGlobs = directoryGlobs;
}
@@ -58,6 +66,7 @@ public:
virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
virtual bool hasFeature(MetaEngineFeature f) const;
+ virtual const ExtraGuiOptions getExtraGuiOptions(const Common::String &target) const;
virtual int getMaximumSaveSlot() const { return Sword25::PersistenceService::getSlotCount(); }
virtual SaveStateList listSaves(const char *target) const;
};
@@ -74,6 +83,12 @@ bool Sword25MetaEngine::hasFeature(MetaEngineFeature f) const {
(f == kSupportsListSaves);
}
+const ExtraGuiOptions Sword25MetaEngine::getExtraGuiOptions(const Common::String &target) const {
+ ExtraGuiOptions options;
+ options.push_back(sword25ExtraGuiOption);
+ return options;
+}
+
SaveStateList Sword25MetaEngine::listSaves(const char *target) const {
Common::String pattern = target;
pattern = pattern + ".???";
diff --git a/engines/sword25/detection_tables.h b/engines/sword25/detection_tables.h
index b58f430fcf..927060bf18 100644
--- a/engines/sword25/detection_tables.h
+++ b/engines/sword25/detection_tables.h
@@ -29,7 +29,7 @@ static const ADGameDescription gameDescriptions[] = {
AD_ENTRY1s("data.b25c", "f8b6e03ada2d2f6cf27fbc11ad1572e9", 654310588),
Common::EN_ANY,
Common::kPlatformUnknown,
- ADGF_TESTING,
+ ADGF_NO_FLAGS,
GUIO1(GUIO_NOASPECT)
},
{
@@ -38,7 +38,7 @@ static const ADGameDescription gameDescriptions[] = {
AD_ENTRY1s("lang_fr.b25c", "690caf157387e06d2c3d1ca53c43f428", 1006043),
Common::FR_FRA,
Common::kPlatformUnknown,
- ADGF_TESTING,
+ ADGF_NO_FLAGS,
GUIO1(GUIO_NOASPECT)
},
{
@@ -47,7 +47,7 @@ static const ADGameDescription gameDescriptions[] = {
AD_ENTRY1s("data.b25c", "f8b6e03ada2d2f6cf27fbc11ad1572e9", 654310588),
Common::DE_DEU,
Common::kPlatformUnknown,
- ADGF_TESTING,
+ ADGF_NO_FLAGS,
GUIO1(GUIO_NOASPECT)
},
{
@@ -56,7 +56,7 @@ static const ADGameDescription gameDescriptions[] = {
AD_ENTRY1s("lang_hr.b25c", "e881054d1f8ec1e527422fc521c25405", 1273217),
Common::HR_HRV,
Common::kPlatformUnknown,
- ADGF_TESTING,
+ ADGF_NO_FLAGS,
GUIO1(GUIO_NOASPECT)
},
{
@@ -65,7 +65,7 @@ static const ADGameDescription gameDescriptions[] = {
AD_ENTRY1s("lang_it.b25c", "f3325666da0515cc2b42062e953c0889", 996197),
Common::IT_ITA,
Common::kPlatformUnknown,
- ADGF_TESTING,
+ ADGF_NO_FLAGS,
GUIO1(GUIO_NOASPECT)
},
{
@@ -74,7 +74,7 @@ static const ADGameDescription gameDescriptions[] = {
AD_ENTRY1s("lang_pl.b25c", "49dc1a20f95391a808e475c49be2bac0", 1281799),
Common::PL_POL,
Common::kPlatformUnknown,
- ADGF_TESTING,
+ ADGF_NO_FLAGS,
GUIO1(GUIO_NOASPECT)
},
{
@@ -83,7 +83,7 @@ static const ADGameDescription gameDescriptions[] = {
AD_ENTRY1s("lang_pt.b25c", "1df701432f9e13dcefe1adeb890b9c69", 993812),
Common::PT_BRA,
Common::kPlatformUnknown,
- ADGF_TESTING,
+ ADGF_NO_FLAGS,
GUIO1(GUIO_NOASPECT)
},
{
@@ -92,7 +92,7 @@ static const ADGameDescription gameDescriptions[] = {
AD_ENTRY1s("lang_ru.b25c", "deb33dd2f90a71ff60181918a8ce5063", 1235378),
Common::RU_RUS,
Common::kPlatformUnknown,
- ADGF_TESTING,
+ ADGF_NO_FLAGS,
GUIO1(GUIO_NOASPECT)
},
{
@@ -101,7 +101,7 @@ static const ADGameDescription gameDescriptions[] = {
AD_ENTRY1s("lang_es.b25c", "384c19072d83725f351bb9ecb4d3f02b", 987965),
Common::ES_ESP,
Common::kPlatformUnknown,
- ADGF_TESTING,
+ ADGF_NO_FLAGS,
GUIO1(GUIO_NOASPECT)
},
// Hungarian "psylog" version.
@@ -112,7 +112,7 @@ static const ADGameDescription gameDescriptions[] = {
AD_ENTRY1s("lang_hu.b25c", "7de51a3b4926a192549e75b1a7d81667", 1864915),
Common::HU_HUN,
Common::kPlatformUnknown,
- ADGF_TESTING,
+ ADGF_NO_FLAGS,
GUIO1(GUIO_NOASPECT)
},
@@ -126,19 +126,22 @@ static const ADGameDescription gameDescriptions[] = {
AD_LISTEND},
Common::EN_ANY,
Common::kPlatformUnknown,
- GF_EXTRACTED | ADGF_TESTING,
+ GF_EXTRACTED | ADGF_NO_FLAGS,
GUIO1(GUIO_NOASPECT)
},
// Distributed by ScummVM
// Contains all language packs, English voice-overs and Hungarian version
+ // Mark it as Unknown Language since it contains multiple languages. If we
+ // mark it as English, then changing the language in-game causes the detection
+ // to fail the next time we try to start the engine.
{
"sword25",
"Latest version",
AD_ENTRY1s("data.b25c", "880a8a67faf4a4e7ab62cf114b771428", 827397764),
- Common::EN_ANY,
+ Common::UNK_LANG,
Common::kPlatformUnknown,
- ADGF_TESTING,
+ ADGF_NO_FLAGS,
GUIO1(GUIO_NOASPECT)
},
diff --git a/engines/sword25/fmv/movieplayer.cpp b/engines/sword25/fmv/movieplayer.cpp
index eb0f0390dc..62a897a332 100644
--- a/engines/sword25/fmv/movieplayer.cpp
+++ b/engines/sword25/fmv/movieplayer.cpp
@@ -58,6 +58,8 @@ MoviePlayer::~MoviePlayer() {
}
bool MoviePlayer::loadMovie(const Common::String &filename, uint z) {
+ if (isMovieLoaded())
+ unloadMovie();
// Get the file and load it into the decoder
Common::SeekableReadStream *in = Kernel::getInstance()->getPackage()->getStream(filename);
_decoder.loadStream(in);
diff --git a/engines/sword25/fmv/movieplayer.h b/engines/sword25/fmv/movieplayer.h
index 95fb05ee60..b59b223b4c 100644
--- a/engines/sword25/fmv/movieplayer.h
+++ b/engines/sword25/fmv/movieplayer.h
@@ -67,7 +67,7 @@ public:
* movie file, it will be unloaded and, if necessary, stopped playing.
* @param Filename The filename of the movie file to be loaded
* @param Z Z indicates the position of the film on the main graphics layer
- * @return Returns false if an error occured while loading, otherwise true.
+ * @return Returns false if an error occurred while loading, otherwise true.
*/
bool loadMovie(const Common::String &filename, uint z);
diff --git a/engines/sword25/gfx/animationresource.cpp b/engines/sword25/gfx/animationresource.cpp
index 431d466658..423a2b86b4 100644
--- a/engines/sword25/gfx/animationresource.cpp
+++ b/engines/sword25/gfx/animationresource.cpp
@@ -211,8 +211,9 @@ bool AnimationResource::precacheAllFrames() const {
error("Could not precache \"%s\".", (*iter).fileName.c_str());
return false;
}
-#else
- Kernel::getInstance()->getResourceManager()->requestResource((*iter).fileName);
+#else
+ Resource *pResource = Kernel::getInstance()->getResourceManager()->requestResource((*iter).fileName);
+ pResource->release(); //unlock precached resource
#endif
}
diff --git a/engines/sword25/gfx/fontresource.cpp b/engines/sword25/gfx/fontresource.cpp
index c4d4c3c52e..1d7aedcb6e 100644
--- a/engines/sword25/gfx/fontresource.cpp
+++ b/engines/sword25/gfx/fontresource.cpp
@@ -103,8 +103,9 @@ bool FontResource::parserCallback_font(ParserNode *node) {
if (!_pKernel->getResourceManager()->precacheResource(_bitmapFileName)) {
error("Could not precache \"%s\".", _bitmapFileName.c_str());
}
-#else
- _pKernel->getResourceManager()->requestResource(_bitmapFileName);
+#else
+ Resource *pResource = _pKernel->getResourceManager()->requestResource(_bitmapFileName);
+ pResource->release(); //unlock precached resource
#endif
return true;
diff --git a/engines/sword25/gfx/text.cpp b/engines/sword25/gfx/text.cpp
index d409c538c0..769c9b1162 100644
--- a/engines/sword25/gfx/text.cpp
+++ b/engines/sword25/gfx/text.cpp
@@ -77,7 +77,8 @@ bool Text::setFont(const Common::String &font) {
return false;
}
#else
- getResourceManager()->requestResource(font);
+ Resource *pResource = getResourceManager()->requestResource(font);
+ pResource->release(); //unlock precached resource
_font = font;
updateFormat();
forceRefresh();
diff --git a/engines/sword25/kernel/kernel_script.cpp b/engines/sword25/kernel/kernel_script.cpp
index 1b7c6a6f14..40dcbd5b98 100644
--- a/engines/sword25/kernel/kernel_script.cpp
+++ b/engines/sword25/kernel/kernel_script.cpp
@@ -257,6 +257,7 @@ static int processMessages(lua_State *L) {
// to the closeWanted() opcode; see also the TODO comment in there.
lua_pushbooleancpp(L, !Engine::shouldQuit());
+ g_system->delayMillis(10);
return 1;
}
diff --git a/engines/sword25/package/packagemanager.cpp b/engines/sword25/package/packagemanager.cpp
index 2db4f2da74..457dda6268 100644
--- a/engines/sword25/package/packagemanager.cpp
+++ b/engines/sword25/package/packagemanager.cpp
@@ -56,7 +56,8 @@ static Common::String normalizePath(const Common::String &path, const Common::St
PackageManager::PackageManager(Kernel *pKernel) : Service(pKernel),
_currentDirectory(PATH_SEPARATOR),
- _rootFolder(ConfMan.get("path")) {
+ _rootFolder(ConfMan.get("path")),
+ _useEnglishSpeech(ConfMan.getBool("english_speech")) {
if (!registerScriptBindings())
error("Script bindings could not be registered.");
else
@@ -71,14 +72,34 @@ PackageManager::~PackageManager() {
}
+Common::String PackageManager::ensureSpeechLang(const Common::String &fileName) {
+ if (!_useEnglishSpeech || fileName.size() < 9 || !fileName.hasPrefix("/speech/"))
+ return fileName;
+
+ // Always keep German speech as a fallback in case the English speech pack is not present.
+ // However this means we cannot play with German text and English voice.
+ if (fileName.hasPrefix("/speech/de"))
+ return fileName;
+
+ Common::String newFileName = "/speech/en";
+ int fileIdx = 9;
+ while (fileIdx < fileName.size() && fileName[fileIdx] != '/')
+ ++fileIdx;
+ if (fileIdx < fileName.size())
+ newFileName += fileName.c_str() + fileIdx;
+
+ return newFileName;
+}
+
/**
* Scans through the archive list for a specified file
*/
Common::ArchiveMemberPtr PackageManager::getArchiveMember(const Common::String &fileName) {
+ Common::String fileName2 = ensureSpeechLang(fileName);
// Loop through checking each archive
Common::List<ArchiveEntry *>::iterator i;
for (i = _archiveList.begin(); i != _archiveList.end(); ++i) {
- if (!fileName.hasPrefix((*i)->_mountPath)) {
+ if (!fileName2.hasPrefix((*i)->_mountPath)) {
// The mount path is in different subtree. Skipping
continue;
}
@@ -87,7 +108,7 @@ Common::ArchiveMemberPtr PackageManager::getArchiveMember(const Common::String &
Common::Archive *archiveFolder = (*i)->archive;
// Construct relative path
- Common::String resPath(&fileName.c_str()[(*i)->_mountPath.size()]);
+ Common::String resPath(&fileName2.c_str()[(*i)->_mountPath.size()]);
if (archiveFolder->hasFile(resPath)) {
return archiveFolder->getMember(resPath);
@@ -203,23 +224,29 @@ bool PackageManager::changeDirectory(const Common::String &directory) {
}
Common::String PackageManager::getAbsolutePath(const Common::String &fileName) {
- return normalizePath(fileName, _currentDirectory);
+ return normalizePath(ensureSpeechLang(fileName), _currentDirectory);
}
bool PackageManager::fileExists(const Common::String &fileName) {
// FIXME: The current Zip implementation doesn't support getting a folder entry, which is needed for detecting
- // the English voick pack
- if (fileName == "/speech/en") {
+ // the English voice pack
+ Common::String fileName2 = ensureSpeechLang(fileName);
+ if (fileName2 == "/speech/en") {
// To get around this, change to detecting one of the files in the folder
- return getArchiveMember(normalizePath(fileName + "/APO0001.ogg", _currentDirectory));
+ bool exists = getArchiveMember(normalizePath(fileName2 + "/APO0001.ogg", _currentDirectory));
+ if (!exists && _useEnglishSpeech) {
+ _useEnglishSpeech = false;
+ warning("English speech not found");
+ }
+ return exists;
}
- Common::ArchiveMemberPtr fileNode = getArchiveMember(normalizePath(fileName, _currentDirectory));
+ Common::ArchiveMemberPtr fileNode = getArchiveMember(normalizePath(fileName2, _currentDirectory));
return fileNode;
}
int PackageManager::doSearch(Common::ArchiveMemberList &list, const Common::String &filter, const Common::String &path, uint typeFilter) {
- Common::String normalizedFilter = normalizePath(filter, _currentDirectory);
+ Common::String normalizedFilter = normalizePath(ensureSpeechLang(filter), _currentDirectory);
int num = 0;
if (path.size() > 0)
diff --git a/engines/sword25/package/packagemanager.h b/engines/sword25/package/packagemanager.h
index a1806a4046..5475cb02fc 100644
--- a/engines/sword25/package/packagemanager.h
+++ b/engines/sword25/package/packagemanager.h
@@ -87,6 +87,9 @@ private:
Common::String _currentDirectory;
Common::FSNode _rootFolder;
Common::List<ArchiveEntry *> _archiveList;
+
+ bool _useEnglishSpeech;
+ Common::String ensureSpeechLang(const Common::String &fileName);
Common::ArchiveMemberPtr getArchiveMember(const Common::String &fileName);
diff --git a/engines/sword25/script/luacallback.cpp b/engines/sword25/script/luacallback.cpp
index 72f7e01612..acfda498c6 100644
--- a/engines/sword25/script/luacallback.cpp
+++ b/engines/sword25/script/luacallback.cpp
@@ -119,7 +119,7 @@ void LuaCallback::invokeCallbackFunctions(lua_State *L, uint objectHandle) {
// Lua_pcall the function and the parameters pop themselves from the stack
if (lua_pcall(L, argumentCount, 0, 0) != 0) {
// An error has occurred
- error("An error occured executing a callback function: %s", lua_tostring(L, -1));
+ error("An error occurred executing a callback function: %s", lua_tostring(L, -1));
// Pop error message from the stack
lua_pop(L, 1);
diff --git a/engines/sword25/script/luascript.cpp b/engines/sword25/script/luascript.cpp
index e93289596b..3aca6676ac 100644
--- a/engines/sword25/script/luascript.cpp
+++ b/engines/sword25/script/luascript.cpp
@@ -214,7 +214,7 @@ bool LuaScriptEngine::executeBuffer(const byte *data, uint size, const Common::S
// Run buffer contents
if (lua_pcall(_state, 0, 0, -2) != 0) {
- error("An error occured while executing \"%s\":\n%s.",
+ error("An error occurred while executing \"%s\":\n%s.",
name.c_str(),
lua_tostring(_state, -1));
lua_pop(_state, 2);
diff --git a/engines/sword25/sword25.cpp b/engines/sword25/sword25.cpp
index 5223481d50..b6f2641714 100644
--- a/engines/sword25/sword25.cpp
+++ b/engines/sword25/sword25.cpp
@@ -120,7 +120,7 @@ Common::Error Sword25Engine::appStart() {
// Pass the command line to the script engine.
ScriptEngine *scriptPtr = Kernel::getInstance()->getScript();
if (!scriptPtr) {
- error("Script intialization failed.");
+ error("Script initialization failed.");
return Common::kUnknownError;
}
diff --git a/engines/sword25/util/lua/lbaselib.cpp b/engines/sword25/util/lua/lbaselib.cpp
index 659c61d956..ec044970ad 100644
--- a/engines/sword25/util/lua/lbaselib.cpp
+++ b/engines/sword25/util/lua/lbaselib.cpp
@@ -492,7 +492,7 @@ static int costatus (lua_State *L, lua_State *co) {
else
return CO_SUS; /* initial state */
}
- default: /* some error occured */
+ default: /* some error occurred */
return CO_DEAD;
}
}
diff --git a/engines/sword25/util/lua/ldo.cpp b/engines/sword25/util/lua/ldo.cpp
index a230097f2a..f4139cb9fc 100644
--- a/engines/sword25/util/lua/ldo.cpp
+++ b/engines/sword25/util/lua/ldo.cpp
@@ -111,10 +111,9 @@ static const char* luaErrorDescription[] = {
void luaD_throw (lua_State *L, int errcode) {
if (L->errorJmp) {
L->errorJmp->status = errcode;
- // LUAI_THROW has been replaced with an error message in ScummVM, together
- // with the LUA error code and description
- //LUAI_THROW(L, L->errorJmp);
- error("LUA error occured, error code is %d (%s)", errcode, luaErrorDescription[errcode]);
+ // LUAI_THROW is sometimes used to ignore the error and restore LUA state
+ LUAI_THROW(L, L->errorJmp);
+ error("LUA error occurred, error code is %d (%s)", errcode, luaErrorDescription[errcode]);
}
else {
L->status = cast_byte(errcode);
diff --git a/engines/sword25/util/lua/luaconf.h b/engines/sword25/util/lua/luaconf.h
index fb85983998..53d0f55290 100644
--- a/engines/sword25/util/lua/luaconf.h
+++ b/engines/sword25/util/lua/luaconf.h
@@ -621,7 +621,7 @@ union luai_Cast { double l_d; long l_l; };
#else
/* default handling with long jumps */
-//#define LUAI_THROW(L,c) longjmp((c)->b, 1) // replaced with error() in ScummVM
+#define LUAI_THROW(L,c) longjmp((c)->b, 1)
#define LUAI_TRY(L,c,a) if (setjmp((c)->b) == 0) { a }
#define luai_jmpbuf jmp_buf
diff --git a/engines/teenagent/detection.cpp b/engines/teenagent/detection.cpp
index 4fd3a06311..caa7bdbec9 100644
--- a/engines/teenagent/detection.cpp
+++ b/engines/teenagent/detection.cpp
@@ -88,7 +88,7 @@ enum {
class TeenAgentMetaEngine : public AdvancedMetaEngine {
public:
TeenAgentMetaEngine() : AdvancedMetaEngine(teenAgentGameDescriptions, sizeof(ADGameDescription), teenAgentGames) {
- _singleid = "teenagent";
+ _singleId = "teenagent";
}
virtual const char *getName() const {
@@ -128,7 +128,6 @@ public:
pattern += ".##";
Common::StringArray filenames = g_system->getSavefileManager()->listSavefiles(pattern);
- Common::sort(filenames.begin(), filenames.end());
SaveStateList saveList;
for (Common::StringArray::const_iterator file = filenames.begin(); file != filenames.end(); ++file) {
@@ -145,6 +144,8 @@ public:
saveList.push_back(SaveStateDescriptor(slot, buf));
}
}
+ // Sort saves based on slot number.
+ Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator());
return saveList;
}
diff --git a/engines/testbed/detection.cpp b/engines/testbed/detection.cpp
index 348ade62b0..7aff7a1805 100644
--- a/engines/testbed/detection.cpp
+++ b/engines/testbed/detection.cpp
@@ -49,7 +49,7 @@ class TestbedMetaEngine : public AdvancedMetaEngine {
public:
TestbedMetaEngine() : AdvancedMetaEngine(testbedDescriptions, sizeof(ADGameDescription), testbed_setting) {
_md5Bytes = 512;
- _singleid = "testbed";
+ _singleId = "testbed";
}
virtual const char *getName() const {
diff --git a/engines/tinsel/detection.cpp b/engines/tinsel/detection.cpp
index 7cb291275c..2fde6e788a 100644
--- a/engines/tinsel/detection.cpp
+++ b/engines/tinsel/detection.cpp
@@ -85,7 +85,7 @@ static const PlainGameDescriptor tinselGames[] = {
class TinselMetaEngine : public AdvancedMetaEngine {
public:
TinselMetaEngine() : AdvancedMetaEngine(Tinsel::gameDescriptions, sizeof(Tinsel::TinselGameDescription), tinselGames) {
- _singleid = "tinsel";
+ _singleId = "tinsel";
}
virtual const char *getName() const {
@@ -136,7 +136,6 @@ SaveStateList TinselMetaEngine::listSaves(const char *target) const {
Common::String pattern = target;
pattern = pattern + ".###";
Common::StringArray files = g_system->getSavefileManager()->listSavefiles(pattern);
- sort(files.begin(), files.end()); // Sort (hopefully ensuring we are sorted numerically..)
SaveStateList saveList;
int slotNum = 0;
@@ -160,6 +159,8 @@ SaveStateList TinselMetaEngine::listSaves(const char *target) const {
}
}
+ // Sort saves based on slot number.
+ Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator());
return saveList;
}
@@ -227,8 +228,8 @@ const ADGameDescription *TinselMetaEngine::fallbackDetect(const FileMap &allFile
// Check which files are included in some dw2 ADGameDescription *and* present
// in fslist without a '1' suffix character. Compute MD5s and file sizes for these files.
- for (g = &Tinsel::gameDescriptions[0]; g->desc.gameid != 0; ++g) {
- if (strcmp(g->desc.gameid, "dw2") != 0)
+ for (g = &Tinsel::gameDescriptions[0]; g->desc.gameId != 0; ++g) {
+ if (strcmp(g->desc.gameId, "dw2") != 0)
continue;
for (fileDesc = g->desc.filesDescriptions; fileDesc->fileName; fileDesc++) {
@@ -264,8 +265,8 @@ const ADGameDescription *TinselMetaEngine::fallbackDetect(const FileMap &allFile
int maxFilesMatched = 0;
// MD5 based matching
- for (g = &Tinsel::gameDescriptions[0]; g->desc.gameid != 0; ++g) {
- if (strcmp(g->desc.gameid, "dw2") != 0)
+ for (g = &Tinsel::gameDescriptions[0]; g->desc.gameId != 0; ++g) {
+ if (strcmp(g->desc.gameId, "dw2") != 0)
continue;
bool fileMissing = false;
diff --git a/engines/tinsel/graphics.cpp b/engines/tinsel/graphics.cpp
index 0282aff3cb..2ff96a9b64 100644
--- a/engines/tinsel/graphics.cpp
+++ b/engines/tinsel/graphics.cpp
@@ -164,7 +164,7 @@ static void t0WrtNonZero(DRAWOBJECT *pObj, uint8 *srcP, uint8 *destP, bool apply
// Horizontal loop
for (int x = 0; x < pObj->width; ) {
- uint32 numBytes = READ_UINT32(srcP);
+ uint32 numBytes = READ_LE_UINT32(srcP);
srcP += sizeof(uint32);
bool repeatFlag = (numBytes & 0x80000000L) != 0;
numBytes &= 0x7fffffff;
diff --git a/engines/tinsel/music.cpp b/engines/tinsel/music.cpp
index 2a9804263a..e0c1f8d1f9 100644
--- a/engines/tinsel/music.cpp
+++ b/engines/tinsel/music.cpp
@@ -381,11 +381,25 @@ MidiMusicPlayer::MidiMusicPlayer(TinselEngine *vm) {
bool milesAudioEnabled = false;
if (vm->getPlatform() == Common::kPlatformDOS) {
- // Enable Miles Audio for DOS only
- milesAudioEnabled = true;
+ // Enable Miles Audio for DOS platform only...
+ switch (vm->getGameID()) {
+ case GID_DW1:
+ if (!vm->getIsADGFDemo()) {
+ // ...for Discworld 1
+ milesAudioEnabled = true;
+ } else {
+ if (vm->isV1CD()) {
+ // ...and for Discworld 1 CD Demo
+ milesAudioEnabled = true;
+ }
+ }
+ break;
+ default:
+ break;
+ }
}
- if ((vm->getGameId() == GID_DW1) && (milesAudioEnabled)) {
+ if (milesAudioEnabled) {
// Discworld 1 (DOS) uses Miles Audio 3
// use our own Miles Audio drivers
//
diff --git a/engines/tinsel/tinsel.cpp b/engines/tinsel/tinsel.cpp
index 77932a5cdf..2adddca4fd 100644
--- a/engines/tinsel/tinsel.cpp
+++ b/engines/tinsel/tinsel.cpp
@@ -820,7 +820,7 @@ const char *const TinselEngine::_textFiles[][3] = {
TinselEngine::TinselEngine(OSystem *syst, const TinselGameDescription *gameDesc) :
Engine(syst), _gameDescription(gameDesc), _random("tinsel"),
- _sound(0), _midiMusic(0), _pcmMusic(0), _bmv(0) {
+ _console(0), _sound(0), _midiMusic(0), _pcmMusic(0), _bmv(0) {
_vm = this;
_config = new Config(this);
diff --git a/engines/toltecs/configure.engine b/engines/toltecs/configure.engine
index be5533efa2..8310a6d6ef 100644
--- a/engines/toltecs/configure.engine
+++ b/engines/toltecs/configure.engine
@@ -1,3 +1,3 @@
# This file is included from the main "configure" script
# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps]
-add_engine toltecs "3 Skulls of the Toltecs" yes
+add_engine toltecs "3 Skulls of the Toltecs" yes "" "" "highres"
diff --git a/engines/toltecs/detection.cpp b/engines/toltecs/detection.cpp
index fb7d8121ff..7c707895e6 100644
--- a/engines/toltecs/detection.cpp
+++ b/engines/toltecs/detection.cpp
@@ -206,7 +206,7 @@ static const ExtraGuiOption toltecsExtraGuiOption = {
class ToltecsMetaEngine : public AdvancedMetaEngine {
public:
ToltecsMetaEngine() : AdvancedMetaEngine(Toltecs::gameDescriptions, sizeof(Toltecs::ToltecsGameDescription), toltecsGames) {
- _singleid = "toltecs";
+ _singleId = "toltecs";
}
virtual const char *getName() const {
@@ -266,7 +266,6 @@ SaveStateList ToltecsMetaEngine::listSaves(const char *target) const {
Common::StringArray filenames;
filenames = saveFileMan->listSavefiles(pattern.c_str());
- Common::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) {
@@ -284,6 +283,8 @@ SaveStateList ToltecsMetaEngine::listSaves(const char *target) const {
}
}
+ // Sort saves based on slot number.
+ Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator());
return saveList;
}
diff --git a/engines/tony/configure.engine b/engines/tony/configure.engine
index f85f45d158..2df4434982 100644
--- a/engines/tony/configure.engine
+++ b/engines/tony/configure.engine
@@ -1,3 +1,3 @@
# This file is included from the main "configure" script
# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps]
-add_engine tony "Tony Tough and the Night of Roasted Moths" yes "" "" "16bit"
+add_engine tony "Tony Tough and the Night of Roasted Moths" yes "" "" "16bit highres"
diff --git a/engines/tony/detection.cpp b/engines/tony/detection.cpp
index 2f05c0abea..ec0b3e186b 100644
--- a/engines/tony/detection.cpp
+++ b/engines/tony/detection.cpp
@@ -118,7 +118,6 @@ SaveStateList TonyMetaEngine::listSaves(const char *target) const {
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) {
@@ -137,6 +136,8 @@ SaveStateList TonyMetaEngine::listSaves(const char *target) const {
}
}
+ // Sort saves based on slot number.
+ Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator());
return saveList;
}
diff --git a/engines/toon/POTFILES b/engines/toon/POTFILES
new file mode 100644
index 0000000000..5bdfa37e5f
--- /dev/null
+++ b/engines/toon/POTFILES
@@ -0,0 +1 @@
+engines/toon/toon.cpp
diff --git a/engines/toon/configure.engine b/engines/toon/configure.engine
index 00c98f7d8a..689bce1c02 100644
--- a/engines/toon/configure.engine
+++ b/engines/toon/configure.engine
@@ -1,3 +1,3 @@
# This file is included from the main "configure" script
# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps]
-add_engine toon "Toonstruck" yes
+add_engine toon "Toonstruck" yes "" "" "highres"
diff --git a/engines/toon/detection.cpp b/engines/toon/detection.cpp
index e38ae3d2f5..5d2e0a9bca 100644
--- a/engines/toon/detection.cpp
+++ b/engines/toon/detection.cpp
@@ -127,7 +127,7 @@ static const char * const directoryGlobs[] = {
class ToonMetaEngine : public AdvancedMetaEngine {
public:
ToonMetaEngine() : AdvancedMetaEngine(Toon::gameDescriptions, sizeof(ADGameDescription), toonGames) {
- _singleid = "toon";
+ _singleId = "toon";
_maxScanDepth = 3;
_directoryGlobs = directoryGlobs;
}
@@ -176,7 +176,6 @@ SaveStateList ToonMetaEngine::listSaves(const char *target) const {
pattern += ".###";
filenames = saveFileMan->listSavefiles(pattern);
- sort(filenames.begin(), filenames.end()); // Sort (hopefully ensuring we are sorted numerically..)
SaveStateList saveList;
for (Common::StringArray::const_iterator filename = filenames.begin(); filename != filenames.end(); ++filename) {
@@ -208,6 +207,8 @@ SaveStateList ToonMetaEngine::listSaves(const char *target) const {
}
}
+ // Sort saves based on slot number.
+ Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator());
return saveList;
}
diff --git a/engines/toon/toon.cpp b/engines/toon/toon.cpp
index 9e2905f454..3ab23a1e51 100644
--- a/engines/toon/toon.cpp
+++ b/engines/toon/toon.cpp
@@ -27,6 +27,7 @@
#include "common/config-manager.h"
#include "common/savefile.h"
#include "common/memstream.h"
+#include "common/translation.h"
#include "engines/advancedDetector.h"
#include "engines/util.h"
@@ -3334,7 +3335,7 @@ bool ToonEngine::saveGame(int32 slot, const Common::String &saveGameDesc) {
Common::String savegameDescription;
if (slot == -1) {
- GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser("Save game:", "Save", true);
+ GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser(_("Save game:"), _("Save"), true);
savegameId = dialog->runModalWithCurrentTarget();
savegameDescription = dialog->getResultString();
delete dialog;
@@ -3426,7 +3427,7 @@ bool ToonEngine::loadGame(int32 slot) {
int16 savegameId;
if (slot == -1) {
- GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser("Restore game:", "Restore", false);
+ GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser(_("Restore game:"), _("Restore"), false);
savegameId = dialog->runModalWithCurrentTarget();
delete dialog;
} else {
diff --git a/engines/touche/configure.engine b/engines/touche/configure.engine
index 777578e623..f35940ef47 100644
--- a/engines/touche/configure.engine
+++ b/engines/touche/configure.engine
@@ -1,3 +1,3 @@
# This file is included from the main "configure" script
# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps]
-add_engine touche "Touche: The Adventures of the Fifth Musketeer" yes
+add_engine touche "Touche: The Adventures of the Fifth Musketeer" yes "" "" "highres"
diff --git a/engines/touche/detection.cpp b/engines/touche/detection.cpp
index 1d0e136d69..dcb58ffae6 100644
--- a/engines/touche/detection.cpp
+++ b/engines/touche/detection.cpp
@@ -128,7 +128,7 @@ class ToucheMetaEngine : public AdvancedMetaEngine {
public:
ToucheMetaEngine() : AdvancedMetaEngine(Touche::gameDescriptions, sizeof(ADGameDescription), toucheGames) {
_md5Bytes = 4096;
- _singleid = "touche";
+ _singleId = "touche";
_maxScanDepth = 2;
_directoryGlobs = directoryGlobs;
}
diff --git a/engines/tsage/blue_force/blueforce_dialogs.cpp b/engines/tsage/blue_force/blueforce_dialogs.cpp
index 5be27c9ae7..3697ca700e 100644
--- a/engines/tsage/blue_force/blueforce_dialogs.cpp
+++ b/engines/tsage/blue_force/blueforce_dialogs.cpp
@@ -161,7 +161,7 @@ void RightClickDialog::execute() {
}
g_system->delayMillis(10);
- GLOBALS._screenSurface.updateScreen();
+ GLOBALS._screen.update();
}
// Deactivate the graphics manager used for the dialog
@@ -242,7 +242,7 @@ void AmmoBeltDialog::execute() {
}
g_system->delayMillis(10);
- GLOBALS._screenSurface.updateScreen();
+ GLOBALS._screen.update();
}
_gfxManager.deactivate();
diff --git a/engines/tsage/blue_force/blueforce_logic.cpp b/engines/tsage/blue_force/blueforce_logic.cpp
index e6e71399dc..2c5f9bd738 100644
--- a/engines/tsage/blue_force/blueforce_logic.cpp
+++ b/engines/tsage/blue_force/blueforce_logic.cpp
@@ -859,7 +859,7 @@ void SceneExt::endStrip() {
}
void SceneExt::clearScreen() {
- BF_GLOBALS._screenSurface.fillRect(BF_GLOBALS._screenSurface.getBounds(), 0);
+ BF_GLOBALS._screen.clear();
}
/*--------------------------------------------------------------------------*/
@@ -1411,7 +1411,7 @@ void SceneMessage::process(Event &event) {
void SceneMessage::draw() {
- GfxSurface &surface = BF_GLOBALS._screenSurface;
+ GfxSurface &surface = BF_GLOBALS._screen;
// Clear the game area
surface.fillRect(Rect(0, 0, SCREEN_WIDTH, UI_INTERFACE_Y), 0);
diff --git a/engines/tsage/blue_force/blueforce_scenes6.cpp b/engines/tsage/blue_force/blueforce_scenes6.cpp
index 92534d3095..0d6b5c2487 100644
--- a/engines/tsage/blue_force/blueforce_scenes6.cpp
+++ b/engines/tsage/blue_force/blueforce_scenes6.cpp
@@ -78,7 +78,7 @@ void Scene600::Action1::signal() {
pObj->animate(ANIM_MODE_NONE, NULL);
}
- BF_GLOBALS._screenSurface.fillRect(BF_GLOBALS._screenSurface.getBounds(), 0);
+ BF_GLOBALS._screen.fillRect(BF_GLOBALS._screen.getBounds(), 0);
scene->loadScene(999);
setDelay(5);
break;
@@ -275,7 +275,7 @@ void Scene666::postInit(SceneObjectList *OwnerList) {
SceneExt::postInit();
BF_GLOBALS._interfaceY = SCREEN_HEIGHT;
loadScene(999);
- BF_GLOBALS._screenSurface.fillRect(BF_GLOBALS._screenSurface.getBounds(), 0);
+ BF_GLOBALS._screen.fillRect(BF_GLOBALS._screen.getBounds(), 0);
if (BF_GLOBALS._dayNumber == 0) {
BF_GLOBALS._dayNumber = 1;
diff --git a/engines/tsage/converse.cpp b/engines/tsage/converse.cpp
index d1faca5dac..7240c91720 100644
--- a/engines/tsage/converse.cpp
+++ b/engines/tsage/converse.cpp
@@ -469,7 +469,7 @@ int ConversationChoiceDialog::execute(const Common::StringArray &choiceList) {
while (!g_globals->_events.getEvent(event, EVENT_KEYPRESS | EVENT_BUTTON_DOWN | EVENT_MOUSE_MOVE) &&
!g_vm->shouldQuit()) {
g_system->delayMillis(10);
- GLOBALS._screenSurface.updateScreen();
+ GLOBALS._screen.update();
}
if (g_vm->shouldQuit())
break;
diff --git a/engines/tsage/core.cpp b/engines/tsage/core.cpp
index d4068c25c9..985d16b031 100644
--- a/engines/tsage/core.cpp
+++ b/engines/tsage/core.cpp
@@ -1467,7 +1467,7 @@ void ScenePalette::fade(const byte *adjustData, bool fullAdjust, int percent) {
// Set the altered palette
g_system->getPaletteManager()->setPalette((const byte *)&tempPalette[0], 0, 256);
- GLOBALS._screenSurface.updateScreen();
+ GLOBALS._screen.update();
}
PaletteRotation *ScenePalette::addRotation(int start, int end, int rotationMode, int duration, Action *action) {
@@ -1524,11 +1524,11 @@ void ScenePalette::changeBackground(const Rect &bounds, FadeMode fadeMode) {
if (g_vm->getGameID() != GType_Ringworld && g_vm->getGameID() != GType_Sherlock1)
tempRect.setHeight(T2_GLOBALS._interfaceY);
- g_globals->_screenSurface.copyFrom(g_globals->_sceneManager._scene->_backSurface,
+ g_globals->_screen.copyFrom(g_globals->_sceneManager._scene->_backSurface,
tempRect, Rect(0, 0, tempRect.width(), tempRect.height()), NULL);
if (g_vm->getGameID() == GType_Ringworld2 && !GLOBALS._player._uiEnabled
&& T2_GLOBALS._interfaceY == UI_INTERFACE_Y) {
- g_globals->_screenSurface.fillRect(Rect(0, UI_INTERFACE_Y, SCREEN_WIDTH, SCREEN_HEIGHT - 1), 0);
+ g_globals->_screen.fillRect(Rect(0, UI_INTERFACE_Y, SCREEN_WIDTH, SCREEN_HEIGHT - 1), 0);
}
for (SynchronizedList<PaletteModifier *>::iterator i = tempPalette._listeners.begin(); i != tempPalette._listeners.end(); ++i)
@@ -1796,7 +1796,7 @@ void SceneItem::display(int resNum, int lineNum, ...) {
// Keep event on-screen until a mouse or keypress
while (!g_vm->shouldQuit() && !g_globals->_events.getEvent(event,
EVENT_BUTTON_DOWN | EVENT_KEYPRESS)) {
- GLOBALS._screenSurface.updateScreen();
+ GLOBALS._screen.update();
g_system->delayMillis(10);
if ((g_vm->getGameID() == GType_Ringworld2) && (R2_GLOBALS._speechSubtitles & SPEECH_VOICE)) {
@@ -2816,7 +2816,7 @@ void SceneObject::updateScreen() {
destRect.translate(-sceneBounds.left, -sceneBounds.top);
srcRect.translate(-g_globals->_sceneOffset.x, -g_globals->_sceneOffset.y);
- g_globals->_screenSurface.copyFrom(g_globals->_sceneManager._scene->_backSurface, srcRect, destRect);
+ g_globals->_screen.copyFrom(g_globals->_sceneManager._scene->_backSurface, srcRect, destRect);
}
}
diff --git a/engines/tsage/detection.cpp b/engines/tsage/detection.cpp
index f85d8433f3..fe555f2fdb 100644
--- a/engines/tsage/detection.cpp
+++ b/engines/tsage/detection.cpp
@@ -40,7 +40,7 @@ struct tSageGameDescription {
};
const char *TSageEngine::getGameId() const {
- return _gameDescription->desc.gameid;
+ return _gameDescription->desc.gameId;
}
uint32 TSageEngine::getGameID() const {
@@ -75,7 +75,7 @@ enum {
class TSageMetaEngine : public AdvancedMetaEngine {
public:
TSageMetaEngine() : AdvancedMetaEngine(TsAGE::gameDescriptions, sizeof(TsAGE::tSageGameDescription), tSageGameTitles) {
- _singleid = "tsage";
+ _singleId = "tsage";
}
virtual const char *getName() const {
@@ -117,7 +117,6 @@ public:
pattern += ".###";
Common::StringArray filenames = g_system->getSavefileManager()->listSavefiles(pattern);
- sort(filenames.begin(), filenames.end());
TsAGE::tSageSavegameHeader header;
SaveStateList saveList;
@@ -141,6 +140,8 @@ public:
}
}
+ // Sort saves based on slot number.
+ Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator());
return saveList;
}
diff --git a/engines/tsage/detection_tables.h b/engines/tsage/detection_tables.h
index 109ac353e6..f331ecdab5 100644
--- a/engines/tsage/detection_tables.h
+++ b/engines/tsage/detection_tables.h
@@ -165,7 +165,7 @@ static const tSageGameDescription gameDescriptions[] = {
AD_ENTRY1s("r2rw.rlb", "df6c25622387007788ca36d99362c1f0", 47586928),
Common::EN_ANY,
Common::kPlatformDOS,
- ADGF_CD | ADGF_TESTING,
+ ADGF_CD,
GUIO0()
},
GType_Ringworld2,
@@ -179,7 +179,7 @@ static const tSageGameDescription gameDescriptions[] = {
AD_ENTRY1s("r2rw.rlb", "c8e1a82c67c3caf57368eadde13dc15f", 32384464),
Common::EN_ANY,
Common::kPlatformDOS,
- ADGF_CD | ADGF_TESTING,
+ ADGF_CD,
GUIO0()
},
GType_Ringworld2,
diff --git a/engines/tsage/events.cpp b/engines/tsage/events.cpp
index 0491c043a4..1fa17022de 100644
--- a/engines/tsage/events.cpp
+++ b/engines/tsage/events.cpp
@@ -50,7 +50,7 @@ bool EventsClass::pollEvent() {
++_frameNumber;
// Update screen
- GLOBALS._screenSurface.updateScreen();
+ GLOBALS._screen.update();
}
if (!g_system->getEventManager()->pollEvent(_event)) return false;
@@ -400,7 +400,7 @@ void EventsClass::delay(int numFrames) {
_priorFrameTime = g_system->getMillis();
}
- GLOBALS._screenSurface.updateScreen();
+ GLOBALS._screen.update();
_prevDelayFrame = _frameNumber;
_priorFrameTime = g_system->getMillis();
}
diff --git a/engines/tsage/globals.cpp b/engines/tsage/globals.cpp
index b880f35007..b95bea3b23 100644
--- a/engines/tsage/globals.cpp
+++ b/engines/tsage/globals.cpp
@@ -59,7 +59,7 @@ static SavedObject *classFactoryProc(const Common::String &className) {
/*--------------------------------------------------------------------------*/
-Globals::Globals() : _dialogCenter(160, 140), _gfxManagerInstance(_screenSurface),
+Globals::Globals() : _dialogCenter(160, 140), _gfxManagerInstance(_screen),
_randomSource("tsage"), _color1(0), _color2(255), _color3(255) {
reset();
_stripNum = 0;
@@ -119,7 +119,7 @@ Globals::Globals() : _dialogCenter(160, 140), _gfxManagerInstance(_screenSurface
_color3 = 4;
_dialogCenter.y = 100;
}
- _screenSurface.setScreenSurface();
+
_gfxManagers.push_back(&_gfxManagerInstance);
_sceneObjects = &_sceneObjectsInstance;
diff --git a/engines/tsage/globals.h b/engines/tsage/globals.h
index 1194fe8b9c..e1ebe261dc 100644
--- a/engines/tsage/globals.h
+++ b/engines/tsage/globals.h
@@ -30,6 +30,7 @@
#include "tsage/events.h"
#include "tsage/sound.h"
#include "tsage/saveload.h"
+#include "tsage/screen.h"
#include "tsage/user_interface.h"
namespace TsAGE {
@@ -38,7 +39,7 @@ class Globals : public SavedObject {
private:
static void dispatchSound(ASound *obj);
public:
- GfxSurface _screenSurface;
+ Screen _screen;
GfxManager _gfxManagerInstance;
Common::List<GfxManager *> _gfxManagers;
SceneHandler *_sceneHandler;
diff --git a/engines/tsage/graphics.cpp b/engines/tsage/graphics.cpp
index 156503fb51..58fa5b8094 100644
--- a/engines/tsage/graphics.cpp
+++ b/engines/tsage/graphics.cpp
@@ -229,123 +229,43 @@ void Rect::synchronize(Serializer &s) {
/*--------------------------------------------------------------------------*/
-GfxSurface::GfxSurface() : _bounds(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT) {
+GfxSurface::GfxSurface() : Graphics::ManagedSurface(), _bounds(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT) {
_disableUpdates = false;
_lockSurfaceCtr = 0;
- _customSurface = NULL;
_transColor = -1;
- _trackDirtyRects = false;
_flags = 0;
}
-GfxSurface::GfxSurface(const GfxSurface &s) {
+GfxSurface::GfxSurface(const GfxSurface &s): Graphics::ManagedSurface() {
_lockSurfaceCtr = 0;
- _customSurface = NULL;
- _trackDirtyRects = false;
- *this = s;
+
+ operator=(s);
}
GfxSurface::~GfxSurface() {
- clear();
+ // Sanity check.. GfxSurface should always be just referencing _rawSurface,
+ // and not directly managing it's own surface
+ assert(disposeAfterUse() == DisposeAfterUse::NO);
}
-void GfxSurface::clear() {
- if (_customSurface) {
- _customSurface->free();
- delete _customSurface;
- _customSurface = NULL;
- }
-}
-
-/**
- * Specifies that the surface will encapsulate the ScummVM screen surface
- */
-void GfxSurface::setScreenSurface() {
- _trackDirtyRects = true;
- create(SCREEN_WIDTH, SCREEN_HEIGHT);
-}
-
-/**
- * Updates the physical screen with the screen surface buffer
- */
-void GfxSurface::updateScreen() {
- assert(_trackDirtyRects);
-
- // Merge any overlapping dirty rects
- mergeDirtyRects();
-
- // Loop through the dirty rect list to copy the affected areas to the sc
- for (Common::List<Rect>::iterator i = _dirtyRects.begin(); i != _dirtyRects.end(); ++i) {
- Rect r = *i;
-
- // Make sure that there is something to update. If not, skip this
- // rectangle. An example case is the speedbike closeup at the beginning
- // of Ringworld (third screen).
- if (r.isEmpty())
- continue;
-
- const byte *srcP = (const byte *)_customSurface->getBasePtr(r.left, r.top);
- g_system->copyRectToScreen(srcP, _customSurface->pitch, r.left, r.top,
- r.width(), r.height());
- }
-
- // Update the physical screen
- g_system->updateScreen();
-
- // Now that the dirty rects have been copied, clear the dirty rect list
- _dirtyRects.clear();
-}
+void GfxSurface::create(uint16 width, uint16 height) {
+ free();
-/**
- * Adds a rect to the dirty rect list
- */
-void GfxSurface::addDirtyRect(const Rect &r) {
- if (_trackDirtyRects) {
- // Get the bounds and adjust to allow for sub-screen areas
- Rect r2 = r;
- r2.translate(_bounds.left, _bounds.top);
-
- // Add to the dirty rect list
- r2.right = MIN(r2.right + 1, SCREEN_WIDTH);
- r2.bottom = MIN(r2.bottom + 1, SCREEN_HEIGHT);
-
- if (r2.isValidRect())
- _dirtyRects.push_back(r2);
- }
+ _rawSurface.create(width, height);
+ setBounds(Rect(0, 0, width, height));
}
-
-
-/**
- * Specifies that the surface should maintain it's own internal surface
- */
-void GfxSurface::create(int width, int height) {
- assert((width >= 0) && (height >= 0));
-
- if (_customSurface) {
- _customSurface->free();
- delete _customSurface;
- }
- _customSurface = new Graphics::Surface();
- _customSurface->create(width, height, Graphics::PixelFormat::createFormatCLUT8());
- Common::fill((byte *)_customSurface->getPixels(), (byte *)_customSurface->getBasePtr(0, height), 0);
- _bounds = Rect(0, 0, width, height);
+void GfxSurface::setBounds(const Rect &bounds) {
+ _bounds = bounds;
+ Graphics::ManagedSurface::create(_rawSurface, bounds);
}
/**
* Locks the surface for access, and returns a raw ScummVM surface to manipulate it
*/
-Graphics::Surface GfxSurface::lockSurface() {
+Graphics::ManagedSurface &GfxSurface::lockSurface() {
++_lockSurfaceCtr;
-
- Graphics::Surface *src = _customSurface;
- assert(src);
-
- // Setup the returned surface either as one pointing to the same pixels as the source, or
- // as a subset of the source one based on the currently set bounds
- Graphics::Surface result;
- result.init(_bounds.width(), _bounds.height(), src->pitch, src->getBasePtr(_bounds.left, _bounds.top), src->format);
- return result;
+ return *this;
}
/**
@@ -367,69 +287,43 @@ void GfxSurface::synchronize(Serializer &s) {
if (s.isSaving()) {
// Save contents of the surface
- if (_customSurface) {
- s.syncAsSint16LE(_customSurface->w);
- s.syncAsSint16LE(_customSurface->h);
- s.syncBytes((byte *)_customSurface->getPixels(), _customSurface->w * _customSurface->h);
+ if (disposeAfterUse() == DisposeAfterUse::YES) {
+ s.syncAsSint16LE(this->w);
+ s.syncAsSint16LE(this->h);
+ s.syncBytes((byte *)getPixels(), this->w * this->h);
} else {
int zero = 0;
s.syncAsSint16LE(zero);
s.syncAsSint16LE(zero);
}
} else {
- int w = 0, h = 0;
- s.syncAsSint16LE(w);
- s.syncAsSint16LE(h);
-
- if ((w == 0) || (h == 0)) {
- if (_customSurface)
- delete _customSurface;
- _customSurface = NULL;
+ int xSize = 0, ySize = 0;
+ s.syncAsSint16LE(xSize);
+ s.syncAsSint16LE(ySize);
+
+ if (xSize == 0 || ySize == 0) {
+ free();
} else {
- create(w, h);
- s.syncBytes((byte *)_customSurface->getPixels(), w * h);
+ create(xSize, ySize);
+ s.syncBytes((byte *)getPixels(), xSize * ySize);
}
}
}
-/**
- * Fills a specified rectangle on the surface with the specified color
- *
- * @bounds Area to fill
- * @color Color to use
- */
-void GfxSurface::fillRect(const Rect &bounds, int color) {
- Graphics::Surface surface = lockSurface();
- surface.fillRect(bounds, color);
- unlockSurface();
- addDirtyRect(bounds);
-}
-
GfxSurface &GfxSurface::operator=(const GfxSurface &s) {
assert(_lockSurfaceCtr == 0);
assert(s._lockSurfaceCtr == 0);
- if (_customSurface) {
- _customSurface->free();
- delete _customSurface;
- }
-
- _customSurface = s._customSurface;
_disableUpdates = s._disableUpdates;
_bounds = s._bounds;
_centroid = s._centroid;
_transColor = s._transColor;
_flags = s._flags;
- if (_customSurface) {
- // Surface owns the internal data, so replicate it so new surface owns it's own
- _customSurface = new Graphics::Surface();
- _customSurface->create(s._customSurface->w, s._customSurface->h, Graphics::PixelFormat::createFormatCLUT8());
- const byte *srcP = (const byte *)s._customSurface->getPixels();
- byte *destP = (byte *)_customSurface->getPixels();
-
- Common::copy(srcP, srcP + (_bounds.width() * _bounds.height()), destP);
- }
+ // Copy the source's surface
+ create(s.w, s.h);
+ blitFrom(s);
+ setBounds(s.getBounds());
return *this;
}
@@ -474,7 +368,7 @@ bool GfxSurface::displayText(const Common::String &msg, const Common::Point &pt)
/**
* Loads a quarter of a screen from a resource
*/
-void GfxSurface::loadScreenSection(Graphics::Surface &dest, int xHalf, int yHalf, int xSection, int ySection) {
+void GfxSurface::loadScreenSection(Graphics::ManagedSurface &dest, int xHalf, int yHalf, int xSection, int ySection) {
int screenNum = g_globals->_sceneManager._scene->_activeScreenNumber;
Rect updateRect(0, 0, 160, 100);
updateRect.translate(xHalf * 160, yHalf * 100);
@@ -682,50 +576,6 @@ void GfxSurface::draw(const Common::Point &pt, Rect *rect) {
}
}
-/**
- * Merges any clipping rectangles that overlap to try and reduce
- * the total number of clip rectangles.
- */
-void GfxSurface::mergeDirtyRects() {
- if (_dirtyRects.size() <= 1)
- return;
-
- Common::List<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
-
- unionRectangle(*rOuter, *rOuter, *rInner);
-
- // remove the inner rect from the list
- _dirtyRects.erase(rInner);
-
- // move back to beginning of list
- rInner = rOuter;
- }
- }
- }
-}
-
-/**
- * Creates the union of two rectangles.
- * Returns True if there is a union.
- * @param pDest destination rectangle that is to receive the new union
- * @param pSrc1 a source rectangle
- * @param pSrc2 a source rectangle
- */
-bool GfxSurface::unionRectangle(Common::Rect &destRect, const Rect &src1, const Rect &src2) {
- destRect = src1;
- destRect.extend(src2);
-
- return !destRect.isEmpty();
-}
-
/*--------------------------------------------------------------------------*/
GfxElement::GfxElement() {
@@ -762,17 +612,16 @@ void GfxElement::highlight() {
Rect tempRect(_bounds);
tempRect.collapse(g_globals->_gfxEdgeAdjust - 1, g_globals->_gfxEdgeAdjust - 1);
- for (int yp = tempRect.top; yp < tempRect.bottom; ++yp) {
- byte *lineP = (byte *)surface.getBasePtr(tempRect.left, yp);
- for (int xp = tempRect.left; xp < tempRect.right; ++xp, ++lineP) {
+ Graphics::Surface dest = surface.getSubArea(tempRect);
+
+ for (int yp = 0; yp < dest.h; ++yp) {
+ byte *lineP = (byte *)dest.getBasePtr(0, yp);
+ for (int xp = 0; xp < tempRect.right; ++xp, ++lineP) {
if (*lineP == _colors.background) *lineP = _colors.foreground;
else if (*lineP == _colors.foreground) *lineP = _colors.background;
}
}
- // Mark the affected area as dirty
- gfxManager.getSurface().addDirtyRect(tempRect);
-
// Release the surface
gfxManager.unlockSurface();
}
@@ -816,10 +665,11 @@ void GfxElement::drawFrame() {
// Loop through the surface area to replace each pixel
// with its proper shaded replacement
- Graphics::Surface surface = gfxManager.lockSurface();
- for (int y = tempRect.top; y < tempRect.bottom; ++y) {
- byte *lineP = (byte *)surface.getBasePtr(tempRect.left, y);
- for (int x = 0; x < tempRect.width(); ++x) {
+ Graphics::Surface dest = gfxManager.getSurface().getSubArea(tempRect);
+
+ for (int y = 0; y < dest.h; ++y) {
+ byte *lineP = (byte *)dest.getBasePtr(0, y);
+ for (int x = 0; x < dest.w; ++x) {
*lineP = transList[*lineP];
lineP++;
}
@@ -827,27 +677,24 @@ void GfxElement::drawFrame() {
// Draw the edge frame
// Outer frame border
- surface.hLine(tempRect.left + 2, tempRect.top, tempRect.right - 2, 0);
- surface.hLine(tempRect.left + 2, tempRect.bottom, tempRect.right - 2, 0);
- surface.vLine(tempRect.left, tempRect.top + 2, tempRect.bottom - 2, 0);
- surface.vLine(tempRect.right, tempRect.top + 2, tempRect.bottom - 2, 0);
- *((byte *)surface.getBasePtr(tempRect.left + 1, tempRect.top + 1)) = 0;
- *((byte *)surface.getBasePtr(tempRect.right - 1, tempRect.top + 1)) = 0;
- *((byte *)surface.getBasePtr(tempRect.left + 1, tempRect.bottom - 1)) = 0;
- *((byte *)surface.getBasePtr(tempRect.right - 1, tempRect.bottom - 1)) = 0;
+ dest.hLine(2, 0, dest.w - 2, 0);
+ dest.hLine(2, dest.h - 1, dest.w - 2, 0);
+ dest.vLine(0, 2, dest.h - 2, 0);
+ dest.vLine(tempRect.right, 2, dest.h - 2, 0);
+ *((byte *)dest.getBasePtr(1, 1)) = 0;
+ *((byte *)dest.getBasePtr(dest.w - 1, 1)) = 0;
+ *((byte *)dest.getBasePtr(1, dest.h - 1)) = 0;
+ *((byte *)dest.getBasePtr(dest.w - 1, dest.h - 1)) = 0;
// Inner frame border
- surface.hLine(tempRect.left + 2, tempRect.top + 1, tempRect.right - 2, R2_GLOBALS._frameEdgeColor);
- surface.hLine(tempRect.left + 2, tempRect.bottom - 1, tempRect.right - 2, R2_GLOBALS._frameEdgeColor);
- surface.vLine(tempRect.left + 1, tempRect.top + 2, tempRect.bottom - 2, R2_GLOBALS._frameEdgeColor);
- surface.vLine(tempRect.right - 1, tempRect.top + 2, tempRect.bottom - 2, R2_GLOBALS._frameEdgeColor);
- *((byte *)surface.getBasePtr(tempRect.left + 2, tempRect.top + 2)) = R2_GLOBALS._frameEdgeColor;
- *((byte *)surface.getBasePtr(tempRect.right - 2, tempRect.top + 2)) = R2_GLOBALS._frameEdgeColor;
- *((byte *)surface.getBasePtr(tempRect.left + 2, tempRect.bottom - 2)) = R2_GLOBALS._frameEdgeColor;
- *((byte *)surface.getBasePtr(tempRect.right - 2, tempRect.bottom - 2)) = R2_GLOBALS._frameEdgeColor;
-
- gfxManager.unlockSurface();
- gfxManager.getSurface().addDirtyRect(tempRect);
+ dest.hLine(2, 1, dest.w - 2, R2_GLOBALS._frameEdgeColor);
+ dest.hLine(2, dest.h - 1, dest.w - 2, R2_GLOBALS._frameEdgeColor);
+ dest.vLine(1, 2, dest.h - 2, R2_GLOBALS._frameEdgeColor);
+ dest.vLine(dest.w - 1, 2, dest.h - 2, R2_GLOBALS._frameEdgeColor);
+ *((byte *)dest.getBasePtr(2, 2)) = R2_GLOBALS._frameEdgeColor;
+ *((byte *)dest.getBasePtr(dest.w - 2, 2)) = R2_GLOBALS._frameEdgeColor;
+ *((byte *)dest.getBasePtr(2, dest.h - 2)) = R2_GLOBALS._frameEdgeColor;
+ *((byte *)dest.getBasePtr(dest.w - 2, dest.h - 2)) = R2_GLOBALS._frameEdgeColor;
} else {
// Fill dialog content with specified background color
@@ -1236,7 +1083,7 @@ GfxButton *GfxDialog::execute(GfxButton *defaultButton) {
}
g_system->delayMillis(10);
- GLOBALS._screenSurface.updateScreen();
+ GLOBALS._screen.update();
}
_gfxManager.deactivate();
@@ -1269,7 +1116,7 @@ void GfxDialog::setPalette() {
/*--------------------------------------------------------------------------*/
-GfxManager::GfxManager() : _surface(g_globals->_screenSurface), _oldManager(NULL) {
+GfxManager::GfxManager() : _surface(g_globals->_screen), _oldManager(NULL) {
_font.setOwner(this);
_font._fillFlag = false;
_bounds = Rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
@@ -1563,23 +1410,24 @@ int GfxFont::writeChar(const char ch) {
int yOffset = (_fontData[charOffset + 1] >> 3) & 0x1f;
const uint8 *dataP = &_fontData[charOffset + 2];
- // Lock the surface for access
- Graphics::Surface surfacePtr = _gfxManager->lockSurface();
-
Rect charRect;
charRect.set(0, 0, charWidth, _fontSize.y);
charRect.translate(_topLeft.x + _position.x, _topLeft.y + _position.y + yOffset);
+ // Get the sub-section of the screen to update
+ Graphics::Surface dest = _gfxManager->getSurface().getSubArea(charRect);
+
if (_fillFlag)
- surfacePtr.fillRect(charRect, _colors.background);
+ dest.fillRect(charRect, _colors.background);
charRect.bottom = charRect.top + charHeight;
+ assert(charRect.height() <= dest.h);
// Display the character
int bitCtr = 0;
uint8 v = 0;
- for (int yp = charRect.top; yp < charRect.bottom; ++yp) {
- byte *destP = (byte *)surfacePtr.getBasePtr(charRect.left, yp);
+ for (int yp = 0; yp < charHeight; ++yp) {
+ byte *destP = (byte *)dest.getBasePtr(0, yp);
for (int xs = 0; xs < charRect.width(); ++xs, ++destP) {
// Get the next color index to use
@@ -1599,13 +1447,9 @@ int GfxFont::writeChar(const char ch) {
}
}
- // Mark the affected area as dirty
- _gfxManager->getSurface().addDirtyRect(charRect);
-
// Move the text writing position
_position.x += charWidth;
- _gfxManager->unlockSurface();
return charWidth;
}
diff --git a/engines/tsage/graphics.h b/engines/tsage/graphics.h
index 25f7aea8cd..3b395b7625 100644
--- a/engines/tsage/graphics.h
+++ b/engines/tsage/graphics.h
@@ -28,7 +28,7 @@
#include "common/list.h"
#include "common/rect.h"
#include "common/system.h"
-#include "graphics/surface.h"
+#include "graphics/managed_surface.h"
namespace TsAGE {
@@ -73,20 +73,13 @@ public:
enum FrameFlag { FRAME_FLIP_CENTROID_X = 4, FRAME_FLIP_CENTROID_Y = 8 };
-class GfxSurface {
+class GfxSurface: virtual public Graphics::ManagedSurface {
private:
- Graphics::Surface *_customSurface;
int _lockSurfaceCtr;
+ Graphics::ManagedSurface _rawSurface;
bool _disableUpdates;
Rect _bounds;
-
- bool _trackDirtyRects;
- Common::List<Rect> _dirtyRects;
-
- void mergeDirtyRects();
- bool unionRectangle(Common::Rect &destRect, const Rect &src1, const Rect &src2);
-
public:
Common::Point _centroid;
int _transColor;
@@ -95,17 +88,13 @@ public:
public:
GfxSurface();
GfxSurface(const GfxSurface &s);
- ~GfxSurface();
+ virtual ~GfxSurface();
- void setScreenSurface();
- void updateScreen();
- void addDirtyRect(const Rect &r);
- Graphics::Surface lockSurface();
+ Graphics::ManagedSurface &lockSurface();
void unlockSurface();
void synchronize(Serializer &s);
- void create(int width, int height);
- void clear();
- void setBounds(const Rect &bounds) { _bounds = bounds; }
+ virtual void create(uint16 width, uint16 height);
+ void setBounds(const Rect &bounds);
const Rect &getBounds() const { return _bounds; }
void copyFrom(GfxSurface &src, Rect srcBounds, Rect destBounds,
@@ -119,10 +108,9 @@ public:
copyFrom(src, tempRect, priorityRegion);
}
void draw(const Common::Point &pt, Rect *rect = NULL);
- void fillRect(const Rect &bounds, int color);
GfxSurface &operator=(const GfxSurface &s);
- static void loadScreenSection(Graphics::Surface &dest, int xHalf, int yHalf, int xSection, int ySection);
+ static void loadScreenSection(Graphics::ManagedSurface &dest, int xHalf, int yHalf, int xSection, int ySection);
static bool displayText(const Common::String &msg, const Common::Point &pt = Common::Point(160, 100));
};
@@ -281,7 +269,7 @@ public:
void getStringBounds(const char *s, Rect &bounds, int maxWidth);
void setDialogPalette();
- Graphics::Surface lockSurface() {
+ Graphics::ManagedSurface lockSurface() {
_surface.setBounds(_bounds);
return _surface.lockSurface();
}
diff --git a/engines/tsage/module.mk b/engines/tsage/module.mk
index e23b157a95..b58c748567 100644
--- a/engines/tsage/module.mk
+++ b/engines/tsage/module.mk
@@ -47,6 +47,7 @@ MODULE_OBJS := \
ringworld2/ringworld2_vampire.o \
saveload.o \
scenes.o \
+ screen.o \
sherlock/sherlock_logo.o \
sound.o \
staticres.o \
diff --git a/engines/tsage/ringworld/ringworld_dialogs.cpp b/engines/tsage/ringworld/ringworld_dialogs.cpp
index 1dd3bc158b..9fa17f3920 100644
--- a/engines/tsage/ringworld/ringworld_dialogs.cpp
+++ b/engines/tsage/ringworld/ringworld_dialogs.cpp
@@ -181,7 +181,7 @@ void RightClickDialog::execute() {
}
g_system->delayMillis(10);
- GLOBALS._screenSurface.updateScreen();
+ GLOBALS._screen.update();
}
_gfxManager.deactivate();
@@ -391,7 +391,7 @@ void InventoryDialog::execute() {
Event event;
while (!g_globals->_events.getEvent(event) && !g_vm->shouldQuit()) {
g_system->delayMillis(10);
- GLOBALS._screenSurface.updateScreen();
+ GLOBALS._screen.update();
}
if (g_vm->shouldQuit())
break;
@@ -439,7 +439,7 @@ void InventoryDialog::execute() {
// Inventory item selected
InvObject *invObject = static_cast<GfxInvImage *>(hiliteObj)->_invObject;
if (lookFlag) {
- g_globals->_screenSurface.displayText(invObject->_description);
+ g_globals->_screen.displayText(invObject->_description);
} else {
RING_INVENTORY._selectedItem = invObject;
invObject->setCursor();
diff --git a/engines/tsage/ringworld/ringworld_logic.cpp b/engines/tsage/ringworld/ringworld_logic.cpp
index 1d8293cffd..354c86abfc 100644
--- a/engines/tsage/ringworld/ringworld_logic.cpp
+++ b/engines/tsage/ringworld/ringworld_logic.cpp
@@ -320,7 +320,7 @@ void SceneArea::wait() {
// Wait until a mouse or keypress
Event event;
while (!g_vm->shouldQuit() && !g_globals->_events.getEvent(event)) {
- GLOBALS._screenSurface.updateScreen();
+ GLOBALS._screen.update();
g_system->delayMillis(10);
}
diff --git a/engines/tsage/ringworld/ringworld_scenes3.cpp b/engines/tsage/ringworld/ringworld_scenes3.cpp
index a515224964..a9ed7af870 100644
--- a/engines/tsage/ringworld/ringworld_scenes3.cpp
+++ b/engines/tsage/ringworld/ringworld_scenes3.cpp
@@ -532,7 +532,7 @@ void Scene2100::Action1::signal() {
// Wait for an event
Event event;
if (!g_globals->_events.getEvent(event)) {
- GLOBALS._screenSurface.updateScreen();
+ GLOBALS._screen.update();
g_system->delayMillis(10);
continue;
}
@@ -2265,7 +2265,7 @@ void Scene2150::Action1::signal() {
// Wait for an event
Event event;
if (!g_globals->_events.getEvent(event)) {
- GLOBALS._screenSurface.updateScreen();
+ GLOBALS._screen.update();
g_system->delayMillis(10);
continue;
}
@@ -5119,7 +5119,7 @@ void Scene2320::Action3::signal() {
// Wait for an event
Event event;
if (!g_globals->_events.getEvent(event)) {
- GLOBALS._screenSurface.updateScreen();
+ GLOBALS._screen.update();
g_system->delayMillis(10);
continue;
}
diff --git a/engines/tsage/ringworld/ringworld_scenes5.cpp b/engines/tsage/ringworld/ringworld_scenes5.cpp
index cb8a89de80..98859f32ee 100644
--- a/engines/tsage/ringworld/ringworld_scenes5.cpp
+++ b/engines/tsage/ringworld/ringworld_scenes5.cpp
@@ -2813,7 +2813,7 @@ void Scene4150::Action1::signal() {
case 4: {
for (int idx = 100; idx >= 0; idx -= 5) {
g_globals->_scenePalette.fade(adjustData, false, idx);
- GLOBALS._screenSurface.updateScreen();
+ GLOBALS._screen.update();
g_system->delayMillis(10);
}
@@ -2841,7 +2841,7 @@ void Scene4150::Action1::signal() {
case 7:
for (int idx = 100; idx >= 0; idx -= 5) {
g_globals->_scenePalette.fade(adjustData, false, idx);
- GLOBALS._screenSurface.updateScreen();
+ GLOBALS._screen.update();
g_system->delayMillis(10);
}
diff --git a/engines/tsage/ringworld2/ringworld2_dialogs.cpp b/engines/tsage/ringworld2/ringworld2_dialogs.cpp
index 99f88a1687..027fb558db 100644
--- a/engines/tsage/ringworld2/ringworld2_dialogs.cpp
+++ b/engines/tsage/ringworld2/ringworld2_dialogs.cpp
@@ -154,7 +154,7 @@ int RightClickDialog::execute() {
}
g_system->delayMillis(10);
- GLOBALS._screenSurface.updateScreen();
+ GLOBALS._screen.update();
}
// Execute the specified action
diff --git a/engines/tsage/ringworld2/ringworld2_logic.cpp b/engines/tsage/ringworld2/ringworld2_logic.cpp
index d24541932f..ecaa671bd7 100644
--- a/engines/tsage/ringworld2/ringworld2_logic.cpp
+++ b/engines/tsage/ringworld2/ringworld2_logic.cpp
@@ -351,7 +351,7 @@ SceneExt::SceneExt(): Scene() {
_preventSaving = false;
// Reset screen clipping area
- R2_GLOBALS._screenSurface._clipRect = Rect();
+ R2_GLOBALS._screen._clipRect = Rect();
// WORKAROUND: In the original, playing animations don't reset the global _animationCtr
// counter as scene changes unless the playing animation explicitly finishes. For now,
@@ -513,7 +513,7 @@ void SceneExt::endStrip() {
}
void SceneExt::clearScreen() {
- R2_GLOBALS._screenSurface.fillRect(R2_GLOBALS._screenSurface.getBounds(), 0);
+ R2_GLOBALS._screen.fillRect(R2_GLOBALS._screen.getBounds(), 0);
}
void SceneExt::refreshBackground(int xAmount, int yAmount) {
@@ -543,15 +543,12 @@ void SceneExt::refreshBackground(int xAmount, int yAmount) {
int screenSize = g_vm->_memoryManager.getSize(dataP);
// Lock the background for update
- Graphics::Surface s = _backSurface.lockSurface();
- assert(screenSize == (s.w * s.h));
+ assert(screenSize == (_backSurface.w * _backSurface.h));
+ Graphics::Surface s = _backSurface.getSubArea(Common::Rect(0, 0, _backSurface.w, _backSurface.h));
- // Copy the data
+ // Copy the data into the surface
byte *destP = (byte *)s.getPixels();
Common::copy(dataP, dataP + (s.w * s.h), destP);
- _backSurface.unlockSurface();
-
- R2_GLOBALS._screenSurface.addDirtyRect(_backSurface.getBounds());
// Free the resource data
DEALLOCATE(dataP);
@@ -601,7 +598,7 @@ void SceneExt::loadBlankScene() {
_backSurface.create(SCREEN_WIDTH, SCREEN_HEIGHT * 3 / 2);
_backSurface.fillRect(_backSurface.getBounds(), 0);
- R2_GLOBALS._screenSurface.fillRect(R2_GLOBALS._screenSurface.getBounds(), 0);
+ R2_GLOBALS._screen.fillRect(R2_GLOBALS._screen.getBounds(), 0);
}
/*--------------------------------------------------------------------------*/
@@ -1966,10 +1963,9 @@ void AnimationPlayer::drawFrame(int sliceIndex) {
byte *sliceData1 = sliceDataStart;
Rect playerBounds = _screenBounds;
- int y = _screenBounds.top;
- R2_GLOBALS._screenSurface.addDirtyRect(playerBounds);
- Graphics::Surface surface = R2_GLOBALS._screenSurface.lockSurface();
+ Graphics::Surface dest = R2_GLOBALS._screen.getSubArea(playerBounds);
+ int y = 0;
// Handle different drawing modes
switch (slice._drawMode) {
@@ -1980,7 +1976,7 @@ void AnimationPlayer::drawFrame(int sliceIndex) {
// TODO: Check of _subData._drawType was done for two different kinds of
// line slice drawing in original
const byte *pSrc = (const byte *)sliceDataStart + READ_LE_UINT16(sliceData1 + sliceNum * 2);
- byte *pDest = (byte *)surface.getBasePtr(playerBounds.left, y++);
+ byte *pDest = (byte *)dest.getBasePtr(0, y++);
Common::copy(pSrc, pSrc + _subData._sliceSize, pDest);
}
@@ -1997,7 +1993,7 @@ void AnimationPlayer::drawFrame(int sliceIndex) {
if (offset) {
const byte *pSrc = (const byte *)sliceDataStart + offset;
- byte *pDest = (byte *)surface.getBasePtr(playerBounds.left, playerBounds.top);
+ byte *pDest = (byte *)dest.getBasePtr(0, 0);
//Common::copy(pSrc, pSrc + playerBounds.width(), pDest);
rleDecode(pSrc, pDest, playerBounds.width());
@@ -2012,7 +2008,7 @@ void AnimationPlayer::drawFrame(int sliceIndex) {
// TODO: Check of _subData._drawType was done for two different kinds of
// line slice drawing in original
const byte *pSrc = (const byte *)sliceDataStart + READ_LE_UINT16(sliceData1 + sliceNum * 2);
- byte *pDest = (byte *)surface.getBasePtr(playerBounds.left, playerBounds.top);
+ byte *pDest = (byte *)dest.getBasePtr(0, 0);
rleDecode(pSrc, pDest, _subData._sliceSize);
}
@@ -2027,7 +2023,7 @@ void AnimationPlayer::drawFrame(int sliceIndex) {
for (int yIndex = 0; yIndex < _sliceHeight; ++yIndex) {
const byte *pSrc1 = (const byte *)sliceDataStart + READ_LE_UINT16(sliceData2 + sliceNum * 2);
const byte *pSrc2 = (const byte *)sliceDataStart + READ_LE_UINT16(sliceData1 + sliceNum * 2);
- byte *pDest = (byte *)surface.getBasePtr(playerBounds.left, y++);
+ byte *pDest = (byte *)dest.getBasePtr(0, y++);
if (slice2._drawMode == 0) {
// Uncompressed background, foreground compressed
@@ -2047,17 +2043,14 @@ void AnimationPlayer::drawFrame(int sliceIndex) {
break;
}
- // Unlock the screen surface
- R2_GLOBALS._screenSurface.unlockSurface();
-
if (_objectMode == ANIMOBJMODE_42) {
_screenBounds.expandPanes();
// Copy the drawn frame to the back surface
- Rect srcRect = R2_GLOBALS._screenSurface.getBounds();
+ Rect srcRect = R2_GLOBALS._screen.getBounds();
Rect destRect = srcRect;
destRect.translate(-g_globals->_sceneOffset.x, -g_globals->_sceneOffset.y);
- R2_GLOBALS._sceneManager._scene->_backSurface.copyFrom(R2_GLOBALS._screenSurface,
+ R2_GLOBALS._sceneManager._scene->_backSurface.copyFrom(R2_GLOBALS._screen,
srcRect, destRect);
// Draw any objects into the scene
diff --git a/engines/tsage/ringworld2/ringworld2_outpost.cpp b/engines/tsage/ringworld2/ringworld2_outpost.cpp
index cad21b4623..8c64970bda 100644
--- a/engines/tsage/ringworld2/ringworld2_outpost.cpp
+++ b/engines/tsage/ringworld2/ringworld2_outpost.cpp
@@ -4689,7 +4689,7 @@ GfxButton *Scene1337::OptionsDialog::execute(GfxButton *defaultButton) {
}
g_system->delayMillis(10);
- GLOBALS._screenSurface.updateScreen();
+ GLOBALS._screen.update();
}
_gfxManager.deactivate();
diff --git a/engines/tsage/ringworld2/ringworld2_scenes0.cpp b/engines/tsage/ringworld2/ringworld2_scenes0.cpp
index 573cbbb29a..63879b0366 100644
--- a/engines/tsage/ringworld2/ringworld2_scenes0.cpp
+++ b/engines/tsage/ringworld2/ringworld2_scenes0.cpp
@@ -1613,7 +1613,7 @@ void Scene180::signal() {
case 43:
case 47:
_helpEnabled = false;
- R2_GLOBALS._screenSurface.fillRect(Rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT), 0);
+ R2_GLOBALS._screen.fillRect(Rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT), 0);
_palette.loadPalette(0);
_palette.loadPalette(9998);
R2_GLOBALS._scenePalette.addFader(_palette._palette, 256, 8, this);
@@ -1815,7 +1815,7 @@ void Scene180::signal() {
_shipDisplay.remove();
_backSurface.fillRect(Rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT), 0);
- R2_GLOBALS._screenSurface.fillRect(Rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT), 0);
+ R2_GLOBALS._screen.fillRect(Rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT), 0);
R2_GLOBALS._sound2.fadeOut2(NULL);
R2_GLOBALS._sound1.fadeOut2(this);
break;
@@ -1880,7 +1880,7 @@ void Scene180::signal() {
R2_GLOBALS._paneRefreshFlag[0] = 3;
_backSurface.fillRect(Rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT), 0);
- R2_GLOBALS._screenSurface.fillRect(Rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT), 0);
+ R2_GLOBALS._screen.fillRect(Rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT), 0);
setSceneDelay(1);
break;
diff --git a/engines/tsage/saveload.cpp b/engines/tsage/saveload.cpp
index 9954b929b2..3cb8e52692 100644
--- a/engines/tsage/saveload.cpp
+++ b/engines/tsage/saveload.cpp
@@ -289,10 +289,10 @@ void Saver::writeSavegameHeader(Common::OutSaveFile *out, tSageSavegameHeader &h
// Create a thumbnail and save it
Graphics::Surface *thumb = new Graphics::Surface();
- Graphics::Surface s = g_globals->_screenSurface.lockSurface();
+ Graphics::Surface s = g_globals->_screen.lockSurface();
::createThumbnail(thumb, (const byte *)s.getPixels(), SCREEN_WIDTH, SCREEN_HEIGHT, thumbPalette);
Graphics::saveThumbnail(*out, *thumb);
- g_globals->_screenSurface.unlockSurface();
+ g_globals->_screen.unlockSurface();
thumb->free();
delete thumb;
diff --git a/engines/tsage/scenes.cpp b/engines/tsage/scenes.cpp
index 80ce1e3ecc..095c0d7ab5 100644
--- a/engines/tsage/scenes.cpp
+++ b/engines/tsage/scenes.cpp
@@ -139,7 +139,7 @@ void SceneManager::fadeInIfNecessary() {
percent = 100;
g_globals->_scenePalette.fade((const byte *)&adjustData, false, percent);
- GLOBALS._screenSurface.updateScreen();
+ GLOBALS._screen.update();
g_system->delayMillis(10);
}
@@ -175,7 +175,7 @@ void SceneManager::changeScene(int newSceneNumber) {
}
// Blank out the screen
- g_globals->_screenSurface.fillRect(g_globals->_screenSurface.getBounds(), 0);
+ g_globals->_screen.fillRect(g_globals->_screen.getBounds(), 0);
// If there are any fading sounds, wait until fading is complete
while (g_globals->_soundManager.isFading()) {
@@ -463,7 +463,7 @@ void Scene::refreshBackground(int xAmount, int yAmount) {
// Check if the section is already loaded
if ((_enabledSections[xp * 16 + yp] == 0xffff) || ((xAmount == 0) && (yAmount == 0))) {
// Chunk isn't loaded, so load it in
- Graphics::Surface s = _backSurface.lockSurface();
+ Graphics::ManagedSurface s = _backSurface.lockSurface();
GfxSurface::loadScreenSection(s, xp - xHalfOffset, yp - yHalfOffset, xp, yp);
_backSurface.unlockSurface();
changedFlag = true;
diff --git a/engines/tsage/screen.cpp b/engines/tsage/screen.cpp
new file mode 100644
index 0000000000..f11c384797
--- /dev/null
+++ b/engines/tsage/screen.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.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 "tsage/screen.h"
+
+namespace TsAGE {
+
+Screen::Screen(): GfxSurface(), Graphics::Screen() {
+ create(SCREEN_WIDTH, SCREEN_HEIGHT);
+}
+
+void Screen::update() {
+ // When dialogs are active, the screen surface may be remapped to
+ // sub-sections of the screen. But for drawing we'll need to temporarily
+ // remove any such remappings and use the entirety of the screen
+ Rect clipBounds = getBounds();
+ setBounds(Rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT));
+
+ // Update the screen
+ Graphics::Screen::update();
+
+ // Reset the clipping
+ setBounds(clipBounds);
+}
+
+} // End of namespace TsAGE
diff --git a/engines/tsage/screen.h b/engines/tsage/screen.h
new file mode 100644
index 0000000000..bf5057e4d6
--- /dev/null
+++ b/engines/tsage/screen.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.
+ *
+ */
+
+#ifndef TSAGE_SCREEN_H
+#define TSAGE_SCREEN_H
+
+#include "common/scummsys.h"
+#include "common/array.h"
+#include "graphics/screen.h"
+#include "tsage/graphics.h"
+
+namespace TsAGE {
+
+#define SCREEN_WIDTH 320
+#define SCREEN_HEIGHT 200
+#define SCREEN_CENTER_X 160
+#define SCREEN_CENTER_Y 100
+#define UI_INTERFACE_Y 168
+
+class Screen : virtual public Graphics::Screen, virtual public GfxSurface {
+public:
+ /**
+ * Constructor
+ */
+ Screen();
+
+ /**
+ * Destructor
+ */
+ virtual ~Screen() {}
+
+ /**
+ * Update the screen
+ */
+ virtual void update();
+};
+
+} // End of namespace TsAGE
+
+#endif /* MADS_SCREEN_H */
diff --git a/engines/tsage/tsage.h b/engines/tsage/tsage.h
index 667a8daa59..1a29700a10 100644
--- a/engines/tsage/tsage.h
+++ b/engines/tsage/tsage.h
@@ -62,12 +62,6 @@ enum {
struct tSageGameDescription;
-#define SCREEN_WIDTH 320
-#define SCREEN_HEIGHT 200
-#define SCREEN_CENTER_X 160
-#define SCREEN_CENTER_Y 100
-#define UI_INTERFACE_Y 168
-
class TSageEngine : public Engine {
private:
const tSageGameDescription *_gameDescription;
diff --git a/engines/tsage/user_interface.cpp b/engines/tsage/user_interface.cpp
index 3ee585d5ef..fffc0dc16c 100644
--- a/engines/tsage/user_interface.cpp
+++ b/engines/tsage/user_interface.cpp
@@ -253,7 +253,7 @@ void UICollection::show() {
void UICollection::erase() {
if (_clearScreen) {
Rect tempRect(0, UI_INTERFACE_Y, SCREEN_WIDTH, SCREEN_HEIGHT);
- GLOBALS._screenSurface.fillRect(tempRect, 0);
+ GLOBALS._screen.fillRect(tempRect, 0);
GLOBALS._sceneManager._scene->_backSurface.fillRect(tempRect, 0);
_clearScreen = false;
}
@@ -274,7 +274,7 @@ void UICollection::draw() {
_objList[idx]->draw();
// Draw the resulting UI onto the screen
- GLOBALS._screenSurface.copyFrom(GLOBALS._sceneManager._scene->_backSurface,
+ GLOBALS._screen.copyFrom(GLOBALS._sceneManager._scene->_backSurface,
Rect(0, UI_INTERFACE_Y, SCREEN_WIDTH, SCREEN_HEIGHT),
Rect(0, UI_INTERFACE_Y, SCREEN_WIDTH, SCREEN_HEIGHT));
@@ -293,12 +293,12 @@ void UICollection::r2rDrawFrame() {
GfxSurface vertLineRight = visage.getFrame(3);
GfxSurface horizLine = visage.getFrame(2);
- GLOBALS._screenSurface.copyFrom(horizLine, 0, 0);
- GLOBALS._screenSurface.copyFrom(vertLineLeft, 0, 3);
- GLOBALS._screenSurface.copyFrom(vertLineRight, SCREEN_WIDTH - 4, 3);
+ GLOBALS._screen.copyFrom(horizLine, 0, 0);
+ GLOBALS._screen.copyFrom(vertLineLeft, 0, 3);
+ GLOBALS._screen.copyFrom(vertLineRight, SCREEN_WIDTH - 4, 3);
// Restrict drawing area to exclude the borders at the edge of the screen
- R2_GLOBALS._screenSurface._clipRect = Rect(4, 3, SCREEN_WIDTH - 4,
+ R2_GLOBALS._screen._clipRect = Rect(4, 3, SCREEN_WIDTH - 4,
SCREEN_HEIGHT - 3);
}
diff --git a/engines/tucker/detection.cpp b/engines/tucker/detection.cpp
index 3d7859e4fd..2447e15d6b 100644
--- a/engines/tucker/detection.cpp
+++ b/engines/tucker/detection.cpp
@@ -116,7 +116,7 @@ class TuckerMetaEngine : public AdvancedMetaEngine {
public:
TuckerMetaEngine() : AdvancedMetaEngine(tuckerGameDescriptions, sizeof(ADGameDescription), tuckerGames) {
_md5Bytes = 512;
- _singleid = "tucker";
+ _singleId = "tucker";
}
virtual const char *getName() const {
@@ -182,6 +182,8 @@ public:
saveList.push_back(SaveStateDescriptor(slot, description));
}
}
+ // Sort saves based on slot number.
+ Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator());
return saveList;
}
diff --git a/engines/tucker/resource.cpp b/engines/tucker/resource.cpp
index 9cba7b523d..d7b75e39c1 100644
--- a/engines/tucker/resource.cpp
+++ b/engines/tucker/resource.cpp
@@ -662,9 +662,11 @@ void TuckerEngine::loadData3() {
void TuckerEngine::loadData4() {
loadFile("data4.c", _loadTempBuf);
DataTokenizer t(_loadTempBuf, _fileLoadSize);
- t.findNextToken(kDataTokenDw);
- _gameDebug = t.getNextInteger() != 0;
- _displayGameHints = t.getNextInteger() != 0;
+ if ((_gameFlags & kGameFlagDemo) == 0) {
+ t.findNextToken(kDataTokenDw);
+ _gameDebug = t.getNextInteger() != 0;
+ _displayGameHints = t.getNextInteger() != 0;
+ }
_locationObjectsCount = 0;
if (t.findIndex(_locationNum)) {
while (t.findNextToken(kDataTokenDw)) {
diff --git a/engines/tucker/sequences.cpp b/engines/tucker/sequences.cpp
index d9f284e443..0151c55eb1 100644
--- a/engines/tucker/sequences.cpp
+++ b/engines/tucker/sequences.cpp
@@ -54,6 +54,7 @@ void TuckerEngine::handleCreditsSequence() {
int counter2 = 0;
int counter1 = 0;
loadCharset2();
+ showCursor(false);
stopSounds();
_locationNum = 74;
_flagsTable[236] = 74;
@@ -159,12 +160,16 @@ void TuckerEngine::handleCreditsSequence() {
redrawScreen(0);
waitForTimer(2);
} while (_fadePaletteCounter > 0);
+ showCursor(true);
}
void TuckerEngine::handleCongratulationsSequence() {
+ // This method is only called right before the program terminates,
+ // so it doesn't bother restoring the palette
_timerCounter2 = 0;
_fadePaletteCounter = 0;
stopSounds();
+ showCursor(false);
loadImage("congrat.pcx", _loadTempBuf, 1);
Graphics::copyRect(_locationBackgroundGfxBuf, 640, _loadTempBuf, 320, 320, 200);
_fullRedraw = true;
@@ -176,11 +181,13 @@ void TuckerEngine::handleCongratulationsSequence() {
}
waitForTimer(3);
}
+ showCursor(true);
}
void TuckerEngine::handleNewPartSequence() {
char filename[40];
+ showCursor(false);
stopSounds();
if (_flagsTable[219] == 1) {
_flagsTable[219] = 0;
@@ -244,7 +251,7 @@ void TuckerEngine::handleNewPartSequence() {
_inputKeys[kInputKeyEscape] = false;
break;
}
- } while (isSpeechSoundPlaying());
+ } while (isSpeechSoundPlaying() && !_quitGame);
stopSpeechSound();
do {
if (_fadePaletteCounter > 0) {
@@ -257,8 +264,9 @@ void TuckerEngine::handleNewPartSequence() {
drawSprite(0);
redrawScreen(0);
waitForTimer(3);
- } while (_fadePaletteCounter > 0);
+ } while (_fadePaletteCounter > 0 && !_quitGame);
_locationNum = currentLocation;
+ showCursor(true);
}
void TuckerEngine::handleMeanwhileSequence() {
@@ -280,8 +288,9 @@ void TuckerEngine::handleMeanwhileSequence() {
strcpy(filename, "loc80.pcx");
}
loadImage(filename, _quadBackgroundGfxBuf + 89600, 1);
+ showCursor(false);
_fadePaletteCounter = 0;
- for (int i = 0; i < 60; ++i) {
+ for (int i = 0; i < 60 && !_quitGame; ++i) {
if (_fadePaletteCounter < 16) {
fadeOutPalette();
++_fadePaletteCounter;
@@ -290,7 +299,10 @@ void TuckerEngine::handleMeanwhileSequence() {
_fullRedraw = true;
redrawScreen(0);
waitForTimer(3);
- ++i;
+ if (_inputKeys[kInputKeyEscape]) {
+ _inputKeys[kInputKeyEscape] = false;
+ break;
+ }
}
do {
if (_fadePaletteCounter > 0) {
@@ -301,9 +313,10 @@ void TuckerEngine::handleMeanwhileSequence() {
_fullRedraw = true;
redrawScreen(0);
waitForTimer(3);
- } while (_fadePaletteCounter > 0);
+ } while (_fadePaletteCounter > 0 && !_quitGame);
memcpy(_currentPalette, backupPalette, 256 * 3);
_fullRedraw = true;
+ showCursor(true);
}
void TuckerEngine::handleMapSequence() {
diff --git a/engines/tucker/tucker.cpp b/engines/tucker/tucker.cpp
index de555cd7b6..ad455c5ded 100644
--- a/engines/tucker/tucker.cpp
+++ b/engines/tucker/tucker.cpp
@@ -710,6 +710,10 @@ void TuckerEngine::setCursorType(int type) {
CursorMan.showMouse(_cursorType < 2);
}
+void TuckerEngine::showCursor(bool visible) {
+ CursorMan.showMouse(visible);
+}
+
void TuckerEngine::setupNewLocation() {
debug(2, "setupNewLocation() current %d next %d", _locationNum, _nextLocationNum);
_locationNum = _nextLocationNum;
diff --git a/engines/tucker/tucker.h b/engines/tucker/tucker.h
index 3bbf6a57f5..2ab94dedbc 100644
--- a/engines/tucker/tucker.h
+++ b/engines/tucker/tucker.h
@@ -300,6 +300,7 @@ protected:
void updateCursorPos(int x, int y);
void setCursorNum(int num);
void setCursorType(int type);
+ void showCursor(bool visible);
void setupNewLocation();
void copyLocBitmap(const char *filename, int offset, bool isMask);
void updateMouseState();
diff --git a/engines/voyeur/animation.cpp b/engines/voyeur/animation.cpp
index 62b37346da..d5d58a2fd3 100644
--- a/engines/voyeur/animation.cpp
+++ b/engines/voyeur/animation.cpp
@@ -470,7 +470,7 @@ void RL2Decoder::play(VoyeurEngine *vm, int resourceOffset,
if (hasDirtyPalette()) {
const byte *palette = getPalette();
- vm->_graphicsManager->setPalette128(palette, paletteStart, paletteCount);
+ vm->_screen->setPalette128(palette, paletteStart, paletteCount);
}
if (needsUpdate()) {
@@ -482,15 +482,14 @@ void RL2Decoder::play(VoyeurEngine *vm, int resourceOffset,
Common::Point pt(READ_LE_UINT16(imgPos + 4 * picCtr) - 32,
READ_LE_UINT16(imgPos + 4 * picCtr + 2) - 20);
- vm->_graphicsManager->sDrawPic(newPic, &videoFrame, pt);
+ vm->_screen->sDrawPic(newPic, &videoFrame, pt);
++picCtr;
}
}
// Decode the next frame and display
const Graphics::Surface *frame = decodeNextFrame();
- Common::copy((const byte *)frame->getPixels(), (const byte *)frame->getPixels() + 320 * 200,
- (byte *)vm->_graphicsManager->_screenSurface.getPixels());
+ vm->_screen->blitFrom(*frame);
}
vm->_eventsManager->getMouseInfo();
diff --git a/engines/voyeur/data.cpp b/engines/voyeur/data.cpp
index b8c987f18b..4d6e32436d 100644
--- a/engines/voyeur/data.cpp
+++ b/engines/voyeur/data.cpp
@@ -240,10 +240,10 @@ void SVoy::reviewAnEvidEvent(int eventIndex) {
int frameOff = e._computerOff;
if (_vm->_bVoy->getBoltGroup(_vm->_playStampGroupId)) {
- _vm->_graphicsManager->_backColors = _vm->_bVoy->boltEntry(_vm->_playStampGroupId + 1)._cMapResource;
- _vm->_graphicsManager->_backgroundPage = _vm->_bVoy->boltEntry(_vm->_playStampGroupId)._picResource;
- _vm->_graphicsManager->_vPort->setupViewPort(_vm->_graphicsManager->_backgroundPage);
- _vm->_graphicsManager->_backColors->startFade();
+ _vm->_screen->_backColors = _vm->_bVoy->boltEntry(_vm->_playStampGroupId + 1)._cMapResource;
+ _vm->_screen->_backgroundPage = _vm->_bVoy->boltEntry(_vm->_playStampGroupId)._picResource;
+ _vm->_screen->_vPort->setupViewPort(_vm->_screen->_backgroundPage);
+ _vm->_screen->_backColors->startFade();
_vm->doEvidDisplay(frameOff, e._dead);
_vm->_bVoy->freeBoltGroup(_vm->_playStampGroupId);
@@ -262,10 +262,10 @@ void SVoy::reviewComputerEvent(int eventIndex) {
_computerTextId = e._computerOn;
if (_vm->_bVoy->getBoltGroup(_vm->_playStampGroupId)) {
- _vm->_graphicsManager->_backColors = _vm->_bVoy->boltEntry(_vm->_playStampGroupId + 1)._cMapResource;
- _vm->_graphicsManager->_backgroundPage = _vm->_bVoy->boltEntry(_vm->_playStampGroupId)._picResource;
- _vm->_graphicsManager->_vPort->setupViewPort(_vm->_graphicsManager->_backgroundPage);
- _vm->_graphicsManager->_backColors->startFade();
+ _vm->_screen->_backColors = _vm->_bVoy->boltEntry(_vm->_playStampGroupId + 1)._cMapResource;
+ _vm->_screen->_backgroundPage = _vm->_bVoy->boltEntry(_vm->_playStampGroupId)._picResource;
+ _vm->_screen->_vPort->setupViewPort(_vm->_screen->_backgroundPage);
+ _vm->_screen->_backColors->startFade();
_vm->flipPageAndWaitForFade();
_vm->getComputerBrush();
diff --git a/engines/voyeur/debugger.cpp b/engines/voyeur/debugger.cpp
index e9a12180da..ebfa123eb6 100644
--- a/engines/voyeur/debugger.cpp
+++ b/engines/voyeur/debugger.cpp
@@ -21,7 +21,7 @@
*/
#include "voyeur/debugger.h"
-#include "voyeur/graphics.h"
+#include "voyeur/screen.h"
#include "voyeur/voyeur.h"
#include "voyeur/staticres.h"
diff --git a/engines/voyeur/detection.cpp b/engines/voyeur/detection.cpp
index 80a23d3c35..9e5320aac8 100644
--- a/engines/voyeur/detection.cpp
+++ b/engines/voyeur/detection.cpp
@@ -117,7 +117,6 @@ SaveStateList VoyeurMetaEngine::listSaves(const char *target) const {
Common::String pattern = Common::String::format("%s.0##", target);
filenames = saveFileMan->listSavefiles(pattern);
- sort(filenames.begin(), filenames.end()); // Sort to get the files in numerical order
SaveStateList saveList;
Voyeur::VoyeurSavegameHeader header;
@@ -139,6 +138,8 @@ SaveStateList VoyeurMetaEngine::listSaves(const char *target) const {
}
}
+ // Sort saves based on slot number.
+ Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator());
return saveList;
}
diff --git a/engines/voyeur/events.cpp b/engines/voyeur/events.cpp
index 34ef507ad3..020fe4b692 100644
--- a/engines/voyeur/events.cpp
+++ b/engines/voyeur/events.cpp
@@ -111,18 +111,18 @@ void EventsManager::mainVoyeurIntFunc() {
}
void EventsManager::sWaitFlip() {
- Common::Array<ViewPortResource *> &viewPorts = _vm->_graphicsManager->_viewPortListPtr->_entries;
+ Common::Array<ViewPortResource *> &viewPorts = _vm->_screen->_viewPortListPtr->_entries;
for (uint idx = 0; idx < viewPorts.size(); ++idx) {
ViewPortResource &viewPort = *viewPorts[idx];
- if (_vm->_graphicsManager->_saveBack && (viewPort._flags & DISPFLAG_40)) {
- Common::Rect *clipPtr = _vm->_graphicsManager->_clipPtr;
- _vm->_graphicsManager->_clipPtr = &viewPort._clipRect;
+ if (_vm->_screen->_saveBack && (viewPort._flags & DISPFLAG_40)) {
+ Common::Rect *clipPtr = _vm->_screen->_clipPtr;
+ _vm->_screen->_clipPtr = &viewPort._clipRect;
if (viewPort._restoreFn)
- (_vm->_graphicsManager->*viewPort._restoreFn)(&viewPort);
+ (_vm->_screen->*viewPort._restoreFn)(&viewPort);
- _vm->_graphicsManager->_clipPtr = clipPtr;
+ _vm->_screen->_clipPtr = clipPtr;
viewPort._rectListCount[viewPort._pageIndex] = 0;
viewPort._rectListPtr[viewPort._pageIndex]->clear();
viewPort._flags &= ~DISPFLAG_40;
@@ -158,9 +158,7 @@ void EventsManager::checkForNextFrameCounter() {
showMousePosition();
// Display the frame
- g_system->copyRectToScreen((byte *)_vm->_graphicsManager->_screenSurface.getPixels(),
- SCREEN_WIDTH, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
- g_system->updateScreen();
+ _vm->_screen->update();
// Signal the ScummVM debugger
_vm->_debugger->onFrame();
@@ -178,10 +176,8 @@ void EventsManager::showMousePosition() {
mousePos += Common::String::format(" - (%d,%d)", pt.x, pt.y);
}
- _vm->_graphicsManager->_screenSurface.fillRect(
- Common::Rect(0, 0, 110, font.getFontHeight()), 0);
- font.drawString(&_vm->_graphicsManager->_screenSurface, mousePos,
- 0, 0, 110, 63);
+ _vm->_screen->fillRect(Common::Rect(0, 0, 110, font.getFontHeight()), 0);
+ font.drawString(_vm->_screen, mousePos, 0, 0, 110, 63);
}
void EventsManager::voyeurTimer() {
@@ -299,11 +295,11 @@ void EventsManager::startFade(CMapResource *cMap) {
if (cMap->_steps > 0) {
_fadeStatus = cMap->_fadeStatus | 1;
- byte *vgaP = &_vm->_graphicsManager->_VGAColors[_fadeFirstCol * 3];
+ byte *vgaP = &_vm->_screen->_VGAColors[_fadeFirstCol * 3];
int mapIndex = 0;
for (int idx = _fadeFirstCol; idx <= _fadeLastCol; ++idx, vgaP += 3) {
- ViewPortPalEntry &palEntry = _vm->_graphicsManager->_viewPortListPtr->_palette[idx];
+ ViewPortPalEntry &palEntry = _vm->_screen->_viewPortListPtr->_palette[idx];
palEntry._rEntry = vgaP[0] << 8;
int rDiff = (cMap->_entries[mapIndex * 3] << 8) - palEntry._rEntry;
palEntry._rChange = rDiff / cMap->_steps;
@@ -325,7 +321,7 @@ void EventsManager::startFade(CMapResource *cMap) {
_intPtr._skipFading = true;
_fadeIntNode._flags &= ~1;
} else {
- byte *vgaP = &_vm->_graphicsManager->_VGAColors[_fadeFirstCol * 3];
+ byte *vgaP = &_vm->_screen->_VGAColors[_fadeFirstCol * 3];
int mapIndex = 0;
for (int idx = _fadeFirstCol; idx <= _fadeLastCol; ++idx, vgaP += 3) {
@@ -371,8 +367,8 @@ void EventsManager::vDoFadeInt() {
}
for (int i = _fadeFirstCol; i <= _fadeLastCol; ++i) {
- ViewPortPalEntry &palEntry = _vm->_graphicsManager->_viewPortListPtr->_palette[i];
- byte *vgaP = &_vm->_graphicsManager->_VGAColors[palEntry._palIndex * 3];
+ ViewPortPalEntry &palEntry = _vm->_screen->_viewPortListPtr->_palette[i];
+ byte *vgaP = &_vm->_screen->_VGAColors[palEntry._palIndex * 3];
palEntry._rEntry += palEntry._rChange;
palEntry._gEntry += palEntry._gChange;
@@ -395,7 +391,7 @@ void EventsManager::vDoCycleInt() {
for (int idx = 3; idx >= 0; --idx) {
if (_cyclePtr->_type[idx] && --_cycleTime[idx] <= 0) {
byte *pSrc = _cycleNext[idx];
- byte *pPal = _vm->_graphicsManager->_VGAColors;
+ byte *pPal = _vm->_screen->_VGAColors;
if (_cyclePtr->_type[idx] != 1) {
// New palette data being specified - loop to set entries
@@ -521,7 +517,7 @@ void EventsManager::setCursor(PictureResource *pic) {
cursor._bounds = pic->_bounds;
cursor._flags = DISPFLAG_CURSOR;
- _vm->_graphicsManager->sDrawPic(pic, &cursor, Common::Point());
+ _vm->_screen->sDrawPic(pic, &cursor, Common::Point());
}
void EventsManager::setCursor(byte *cursorData, int width, int height, int keyColor) {
@@ -531,16 +527,16 @@ void EventsManager::setCursor(byte *cursorData, int width, int height, int keyCo
void EventsManager::setCursorColor(int idx, int mode) {
switch (mode) {
case 0:
- _vm->_graphicsManager->setColor(idx, 90, 90, 232);
+ _vm->_screen->setColor(idx, 90, 90, 232);
break;
case 1:
- _vm->_graphicsManager->setColor(idx, 232, 90, 90);
+ _vm->_screen->setColor(idx, 232, 90, 90);
break;
case 2:
- _vm->_graphicsManager->setColor(idx, 90, 232, 90);
+ _vm->_screen->setColor(idx, 90, 232, 90);
break;
case 3:
- _vm->_graphicsManager->setColor(idx, 90, 232, 232);
+ _vm->_screen->setColor(idx, 90, 232, 232);
break;
default:
break;
@@ -564,12 +560,12 @@ void EventsManager::getMouseInfo() {
if (_cursorBlinked) {
_cursorBlinked = false;
- _vm->_graphicsManager->setOneColor(128, 220, 20, 20);
- _vm->_graphicsManager->setColor(128, 220, 20, 20);
+ _vm->_screen->setOneColor(128, 220, 20, 20);
+ _vm->_screen->setColor(128, 220, 20, 20);
} else {
_cursorBlinked = true;
- _vm->_graphicsManager->setOneColor(128, 220, 220, 220);
- _vm->_graphicsManager->setColor(128, 220, 220, 220);
+ _vm->_screen->setOneColor(128, 220, 220, 220);
+ _vm->_screen->setColor(128, 220, 220, 220);
}
}
}
@@ -585,11 +581,11 @@ void EventsManager::getMouseInfo() {
void EventsManager::startCursorBlink() {
if (_vm->_voy->_eventFlags & EVTFLAG_RECORDING) {
- _vm->_graphicsManager->setOneColor(128, 55, 5, 5);
- _vm->_graphicsManager->setColor(128, 220, 20, 20);
+ _vm->_screen->setOneColor(128, 55, 5, 5);
+ _vm->_screen->setColor(128, 220, 20, 20);
_intPtr._hasPalette = true;
- _vm->_graphicsManager->drawDot();
+ _vm->_screen->drawDot();
//copySection();
}
}
diff --git a/engines/voyeur/files.cpp b/engines/voyeur/files.cpp
index 300e086f75..46b195ecaf 100644
--- a/engines/voyeur/files.cpp
+++ b/engines/voyeur/files.cpp
@@ -21,7 +21,7 @@
*/
#include "voyeur/files.h"
-#include "voyeur/graphics.h"
+#include "voyeur/screen.h"
#include "voyeur/voyeur.h"
#include "voyeur/staticres.h"
@@ -359,7 +359,7 @@ void BoltFile::resolveIt(uint32 id, byte **p) {
}
}
-void BoltFile::resolveFunction(uint32 id, GraphicMethodPtr *fn) {
+void BoltFile::resolveFunction(uint32 id, ScreenMethodPtr *fn) {
if ((int32)id == -1)
*fn = NULL;
else
@@ -485,8 +485,8 @@ void BVoyBoltFile::initViewPortList() {
_state._curMemberPtr->_viewPortListResource = res = new ViewPortListResource(
_state, _state._curMemberPtr->_data);
- _state._vm->_graphicsManager->_viewPortListPtr = res;
- _state._vm->_graphicsManager->_vPort = res->_entries[0];
+ _state._vm->_screen->_viewPortListPtr = res;
+ _state._vm->_screen->_vPort = res->_entries[0];
}
void BVoyBoltFile::initFontInfo() {
@@ -752,24 +752,24 @@ DisplayResource::DisplayResource(VoyeurEngine *vm) {
void DisplayResource::sFillBox(int width, int height) {
assert(_vm);
- bool saveBack = _vm->_graphicsManager->_saveBack;
- _vm->_graphicsManager->_saveBack = false;
+ bool saveBack = _vm->_screen->_saveBack;
+ _vm->_screen->_saveBack = false;
PictureResource pr;
pr._flags = DISPFLAG_1;
pr._select = 0xff;
pr._pick = 0;
- pr._onOff = _vm->_graphicsManager->_drawPtr->_penColor;
+ pr._onOff = _vm->_screen->_drawPtr->_penColor;
pr._bounds = Common::Rect(0, 0, width, height);
- _vm->_graphicsManager->sDrawPic(&pr, this, _vm->_graphicsManager->_drawPtr->_pos);
- _vm->_graphicsManager->_saveBack = saveBack;
+ _vm->_screen->sDrawPic(&pr, this, _vm->_screen->_drawPtr->_pos);
+ _vm->_screen->_saveBack = saveBack;
}
bool DisplayResource::clipRect(Common::Rect &rect) {
Common::Rect clippingRect;
- if (_vm->_graphicsManager->_clipPtr) {
- clippingRect = *_vm->_graphicsManager->_clipPtr;
+ if (_vm->_screen->_clipPtr) {
+ clippingRect = *_vm->_screen->_clipPtr;
} else if (_flags & DISPFLAG_VIEWPORT) {
clippingRect = ((ViewPortResource *)this)->_clipRect;
} else {
@@ -804,18 +804,18 @@ bool DisplayResource::clipRect(Common::Rect &rect) {
}
int DisplayResource::drawText(const Common::String &msg) {
- GraphicsManager &gfxManager = *_vm->_graphicsManager;
- assert(gfxManager._fontPtr);
- assert(gfxManager._fontPtr->_curFont);
- FontInfoResource &fontInfo = *gfxManager._fontPtr;
- PictureResource &fontChar = *_vm->_graphicsManager->_fontChar;
+ Screen &screen = *_vm->_screen;
+ assert(screen._fontPtr);
+ assert(screen._fontPtr->_curFont);
+ FontInfoResource &fontInfo = *screen._fontPtr;
+ PictureResource &fontChar = *_vm->_screen->_fontChar;
FontResource &fontData = *fontInfo._curFont;
int xShadows[9] = { 0, 1, 1, 1, 0, -1, -1, -1, 0 };
int yShadows[9] = { 0, 1, 0, -1, -1, -1, 0, 1, 1 };
- Common::Rect *clipPtr = gfxManager._clipPtr;
+ Common::Rect *clipPtr = screen._clipPtr;
if (!(fontInfo._picFlags & DISPFLAG_1))
- gfxManager._clipPtr = NULL;
+ screen._clipPtr = NULL;
int minChar = fontData._minChar;
int padding = fontData._padding;
@@ -834,7 +834,7 @@ int DisplayResource::drawText(const Common::String &msg) {
(ViewPortResource *)this;
if ((fontInfo._fontFlags & DISPFLAG_1) || fontInfo._justify ||
- (gfxManager._saveBack && fontInfo._fontSaveBack && (_flags & DISPFLAG_VIEWPORT))) {
+ (screen._saveBack && fontInfo._fontSaveBack && (_flags & DISPFLAG_VIEWPORT))) {
msgWidth = viewPort->textWidth(msg);
yp = pos.y;
xp = pos.x;
@@ -898,18 +898,18 @@ int DisplayResource::drawText(const Common::String &msg) {
}
}
- if (gfxManager._saveBack && fontInfo._fontSaveBack && (_flags & DISPFLAG_VIEWPORT)) {
+ if (screen._saveBack && fontInfo._fontSaveBack && (_flags & DISPFLAG_VIEWPORT)) {
viewPort->addSaveRect(viewPort->_pageIndex, viewPort->_fontRect);
}
if (fontInfo._fontFlags & DISPFLAG_1) {
- gfxManager._drawPtr->_pos = Common::Point(viewPort->_fontRect.left, viewPort->_fontRect.top);
- gfxManager._drawPtr->_penColor = fontInfo._backColor;
+ screen._drawPtr->_pos = Common::Point(viewPort->_fontRect.left, viewPort->_fontRect.top);
+ screen._drawPtr->_penColor = fontInfo._backColor;
sFillBox(viewPort->_fontRect.width(), viewPort->_fontRect.height());
}
- bool saveBack = gfxManager._saveBack;
- gfxManager._saveBack = false;
+ bool saveBack = screen._saveBack;
+ screen._saveBack = false;
int count = 0;
if (fontInfo._fontFlags & DISPFLAG_4)
@@ -970,7 +970,7 @@ int DisplayResource::drawText(const Common::String &msg) {
uint16 offset = READ_LE_UINT16(fontData._charOffsets + charValue * 2);
fontChar._imgData = fontData._charImages + offset * 2;
- gfxManager.sDrawPic(&fontChar, this, Common::Point(xp, yp));
+ screen.sDrawPic(&fontChar, this, Common::Point(xp, yp));
fontChar._imgData = NULL;
xp += charWidth + padding;
@@ -982,8 +982,8 @@ int DisplayResource::drawText(const Common::String &msg) {
if (fontInfo._justify == ALIGN_LEFT)
fontInfo._pos.x = xp;
- gfxManager._saveBack = saveBack;
- gfxManager._clipPtr = clipPtr;
+ screen._saveBack = saveBack;
+ screen._clipPtr = clipPtr;
return msgWidth;
}
@@ -993,7 +993,7 @@ int DisplayResource::textWidth(const Common::String &msg) {
return 0;
const char *msgP = msg.c_str();
- FontResource &fontData = *_vm->_graphicsManager->_fontPtr->_curFont;
+ FontResource &fontData = *_vm->_screen->_fontPtr->_curFont;
int minChar = fontData._minChar;
int maxChar = fontData._maxChar;
int padding = fontData._padding;
@@ -1085,9 +1085,9 @@ PictureResource::PictureResource(BoltFilesState &state, const byte *src):
mode = 226;
}
- if (mode != state._vm->_graphicsManager->_SVGAMode) {
- state._vm->_graphicsManager->_SVGAMode = mode;
- state._vm->_graphicsManager->clearPalette();
+ if (mode != state._vm->_screen->_SVGAMode) {
+ state._vm->_screen->_SVGAMode = mode;
+ state._vm->_screen->clearPalette();
}
int screenOffset = READ_LE_UINT32(&src[18]) & 0xffff;
@@ -1096,13 +1096,14 @@ PictureResource::PictureResource(BoltFilesState &state, const byte *src):
if (_flags & PICFLAG_CLEAR_SCREEN) {
// Clear screen picture. That's right. This game actually has a picture
// resource flag to clear the screen! Bizarre.
- Graphics::Surface &s = state._vm->_graphicsManager->_screenSurface;
- s.fillRect(Common::Rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT), 0);
+ state._vm->_screen->clear();
} else {
// Direct screen loading picture. In this case, the raw data of the resource
// is directly decompressed into the screen surface. Again, bizarre.
- byte *pDest = (byte *)state._vm->_graphicsManager->_screenSurface.getPixels();
+ Screen &screen = *state._vm->_screen;
+ byte *pDest = (byte *)screen.getPixels();
state.decompress(pDest, SCREEN_WIDTH * SCREEN_HEIGHT, state._curMemberPtr->_mode);
+ screen.markAllDirty();
}
} else {
if (_flags & PICFLAG_CLEAR_SCREEN00) {
@@ -1249,13 +1250,13 @@ ViewPortResource::ViewPortResource(BoltFilesState &state, const byte *src):
ys + READ_LE_UINT16(src + 0x4C));
state._curLibPtr->resolveIt(READ_LE_UINT32(src + 0x7A), &dummy);
- state._curLibPtr->resolveFunction(READ_LE_UINT32(src + 0x7E), (GraphicMethodPtr *)&_fn1);
- state._curLibPtr->resolveFunction(READ_LE_UINT32(src + 0x82), (GraphicMethodPtr *)&_setupFn);
- state._curLibPtr->resolveFunction(READ_LE_UINT32(src + 0x86), (GraphicMethodPtr *)&_addFn);
- state._curLibPtr->resolveFunction(READ_LE_UINT32(src + 0x8A), (GraphicMethodPtr *)&_restoreFn);
+ state._curLibPtr->resolveFunction(READ_LE_UINT32(src + 0x7E), (ScreenMethodPtr *)&_fn1);
+ state._curLibPtr->resolveFunction(READ_LE_UINT32(src + 0x82), (ScreenMethodPtr *)&_setupFn);
+ state._curLibPtr->resolveFunction(READ_LE_UINT32(src + 0x86), (ScreenMethodPtr *)&_addFn);
+ state._curLibPtr->resolveFunction(READ_LE_UINT32(src + 0x8A), (ScreenMethodPtr *)&_restoreFn);
if (!_restoreFn && _addFn)
- _addFn = &GraphicsManager::addRectNoSaveBack;
+ _addFn = &Screen::addRectNoSaveBack;
}
ViewPortResource::~ViewPortResource() {
@@ -1327,19 +1328,19 @@ void ViewPortResource::setupViewPort(PictureResource *page, Common::Rect *clippi
_restoreFn = restoreFn;
if (setupFn)
- (_state._vm->_graphicsManager->*setupFn)(this);
+ (_state._vm->_screen->*setupFn)(this);
}
void ViewPortResource::setupViewPort() {
- setupViewPort(_state._vm->_graphicsManager->_backgroundPage, NULL,
- &GraphicsManager::setupMCGASaveRect, &GraphicsManager::addRectOptSaveRect,
- &GraphicsManager::restoreMCGASaveRect);
+ setupViewPort(_state._vm->_screen->_backgroundPage, NULL,
+ &Screen::setupMCGASaveRect, &Screen::addRectOptSaveRect,
+ &Screen::restoreMCGASaveRect);
}
void ViewPortResource::setupViewPort(PictureResource *pic, Common::Rect *clippingRect) {
setupViewPort(pic, clippingRect,
- &GraphicsManager::setupMCGASaveRect, &GraphicsManager::addRectOptSaveRect,
- &GraphicsManager::restoreMCGASaveRect);
+ &Screen::setupMCGASaveRect, &Screen::addRectOptSaveRect,
+ &Screen::restoreMCGASaveRect);
}
void ViewPortResource::addSaveRect(int pageIndex, const Common::Rect &r) {
@@ -1347,7 +1348,7 @@ void ViewPortResource::addSaveRect(int pageIndex, const Common::Rect &r) {
if (clipRect(rect)) {
if (_addFn) {
- (_state._vm->_graphicsManager->*_addFn)(this, pageIndex, rect);
+ (_state._vm->_screen->*_addFn)(this, pageIndex, rect);
} else if (_rectListCount[pageIndex] != -1) {
_rectListPtr[pageIndex]->push_back(rect);
}
@@ -1355,26 +1356,26 @@ void ViewPortResource::addSaveRect(int pageIndex, const Common::Rect &r) {
}
void ViewPortResource::fillPic(byte onOff) {
- _state._vm->_graphicsManager->fillPic(this, onOff);
+ _state._vm->_screen->fillPic(this, onOff);
}
void ViewPortResource::drawIfaceTime() {
// Hour display
- _state._vm->_graphicsManager->drawANumber(_state._vm->_graphicsManager->_vPort,
+ _state._vm->_screen->drawANumber(_state._vm->_screen->_vPort,
(_state._vm->_gameHour / 10) == 0 ? 10 : _state._vm->_gameHour / 10,
Common::Point(161, 25));
- _state._vm->_graphicsManager->drawANumber(_state._vm->_graphicsManager->_vPort,
+ _state._vm->_screen->drawANumber(_state._vm->_screen->_vPort,
_state._vm->_gameHour % 10, Common::Point(172, 25));
// Minute display
- _state._vm->_graphicsManager->drawANumber(_state._vm->_graphicsManager->_vPort,
+ _state._vm->_screen->drawANumber(_state._vm->_screen->_vPort,
_state._vm->_gameMinute / 10, Common::Point(190, 25));
- _state._vm->_graphicsManager->drawANumber(_state._vm->_graphicsManager->_vPort,
+ _state._vm->_screen->drawANumber(_state._vm->_screen->_vPort,
_state._vm->_gameMinute % 10, Common::Point(201, 25));
// AM/PM indicator
PictureResource *pic = _state._vm->_bVoy->boltEntry(_state._vm->_voy->_isAM ? 272 : 273)._picResource;
- _state._vm->_graphicsManager->sDrawPic(pic, _state._vm->_graphicsManager->_vPort,
+ _state._vm->_screen->sDrawPic(pic, _state._vm->_screen->_vPort,
Common::Point(215, 27));
}
@@ -1382,9 +1383,9 @@ void ViewPortResource::drawPicPerm(PictureResource *pic, const Common::Point &pt
Common::Rect bounds = pic->_bounds;
bounds.translate(pt.x, pt.y);
- bool saveBack = _state._vm->_graphicsManager->_saveBack;
- _state._vm->_graphicsManager->_saveBack = false;
- _state._vm->_graphicsManager->sDrawPic(pic, this, pt);
+ bool saveBack = _state._vm->_screen->_saveBack;
+ _state._vm->_screen->_saveBack = false;
+ _state._vm->_screen->sDrawPic(pic, this, pt);
clipRect(bounds);
for (int pageIndex = 0; pageIndex < _pageCount; ++pageIndex) {
@@ -1393,7 +1394,7 @@ void ViewPortResource::drawPicPerm(PictureResource *pic, const Common::Point &pt
}
}
- _state._vm->_graphicsManager->_saveBack = saveBack;
+ _state._vm->_screen->_saveBack = saveBack;
}
/*------------------------------------------------------------------------*/
@@ -1526,7 +1527,7 @@ CMapResource::CMapResource(BoltFilesState &state, const byte *src): _vm(state._v
_entries = new byte[count * 3];
Common::copy(src + 6, src + 6 + 3 * count, _entries);
- int palIndex = state._vm->_graphicsManager->_viewPortListPtr->_palIndex;
+ int palIndex = state._vm->_screen->_viewPortListPtr->_palIndex;
if (_end > palIndex)
_end = palIndex;
if (_start > palIndex)
diff --git a/engines/voyeur/files.h b/engines/voyeur/files.h
index eef5df497c..8726b38ddf 100644
--- a/engines/voyeur/files.h
+++ b/engines/voyeur/files.h
@@ -27,7 +27,7 @@
#include "common/file.h"
#include "common/rect.h"
#include "common/str.h"
-#include "voyeur/graphics.h"
+#include "voyeur/screen.h"
namespace Voyeur {
@@ -112,7 +112,7 @@ public:
byte *memberAddr(uint32 id);
byte *memberAddrOffset(uint32 id);
void resolveIt(uint32 id, byte **p);
- void resolveFunction(uint32 id, GraphicMethodPtr *fn);
+ void resolveFunction(uint32 id, ScreenMethodPtr *fn);
BoltEntry &boltEntry(uint16 id);
BoltEntry &getBoltEntryFromLong(uint32 id);
@@ -340,7 +340,7 @@ public:
int _rectListCount[3];
Common::Rect _clipRect;
- GraphicMethodPtr _fn1;
+ ScreenMethodPtr _fn1;
ViewPortSetupPtr _setupFn;
ViewPortAddPtr _addFn;
ViewPortRestorePtr _restoreFn;
diff --git a/engines/voyeur/files_threads.cpp b/engines/voyeur/files_threads.cpp
index 9908324043..bbd3dfe4e9 100644
--- a/engines/voyeur/files_threads.cpp
+++ b/engines/voyeur/files_threads.cpp
@@ -21,7 +21,7 @@
*/
#include "voyeur/files.h"
-#include "voyeur/graphics.h"
+#include "voyeur/screen.h"
#include "voyeur/voyeur.h"
#include "voyeur/staticres.h"
@@ -461,7 +461,7 @@ void ThreadResource::parsePlayCommands() {
pic = _vm->_bVoy->boltEntry(_vm->_playStampGroupId + i * 2)._picResource;
pal = _vm->_bVoy->boltEntry(_vm->_playStampGroupId + i * 2 + 1)._cMapResource;
- _vm->_graphicsManager->_vPort->setupViewPort(pic);
+ _vm->_screen->_vPort->setupViewPort(pic);
pal->startFade();
_vm->flipPageAndWaitForFade();
@@ -980,10 +980,10 @@ int ThreadResource::doApt() {
_vm->_soundManager->startVOCPlay(_vm->_soundManager->getVOCFileName(_vm->_currentVocId));
_vm->_currentVocId = 151;
- _vm->_graphicsManager->setColor(129, 82, 82, 82);
- _vm->_graphicsManager->setColor(130, 112, 112, 112);
- _vm->_graphicsManager->setColor(131, 215, 215, 215);
- _vm->_graphicsManager->setColor(132, 235, 235, 235);
+ _vm->_screen->setColor(129, 82, 82, 82);
+ _vm->_screen->setColor(130, 112, 112, 112);
+ _vm->_screen->setColor(131, 215, 215, 215);
+ _vm->_screen->setColor(132, 235, 235, 235);
_vm->_eventsManager->_intPtr._hasPalette = true;
@@ -1044,7 +1044,7 @@ int ThreadResource::doApt() {
// Draw the text description for the highlighted hotspot
pic = _vm->_bVoy->boltEntry(_vm->_playStampGroupId +
hotspotId + 6)._picResource;
- _vm->_graphicsManager->sDrawPic(pic, _vm->_graphicsManager->_vPort,
+ _vm->_screen->sDrawPic(pic, _vm->_screen->_vPort,
Common::Point(106, 200));
}
@@ -1112,10 +1112,10 @@ void ThreadResource::doRoom() {
if (!vm._bVoy->getBoltGroup(vm._playStampGroupId))
return;
- vm._graphicsManager->_backColors = vm._bVoy->boltEntry(vm._playStampGroupId + 1)._cMapResource;
- vm._graphicsManager->_backgroundPage = vm._bVoy->boltEntry(vm._playStampGroupId)._picResource;
- vm._graphicsManager->_vPort->setupViewPort(vm._graphicsManager->_backgroundPage);
- vm._graphicsManager->_backColors->startFade();
+ vm._screen->_backColors = vm._bVoy->boltEntry(vm._playStampGroupId + 1)._cMapResource;
+ vm._screen->_backgroundPage = vm._bVoy->boltEntry(vm._playStampGroupId)._picResource;
+ vm._screen->_vPort->setupViewPort(vm._screen->_backgroundPage);
+ vm._screen->_backColors->startFade();
voy._fadingStep1 = 2;
voy._fadingStep2 = 0;
@@ -1144,7 +1144,7 @@ void ThreadResource::doRoom() {
bool breakFlag = false;
while (!vm.shouldQuit() && !breakFlag) {
_vm->_voyeurArea = AREA_ROOM;
- vm._graphicsManager->setColor(128, 0, 255, 0);
+ vm._screen->setColor(128, 0, 255, 0);
vm._eventsManager->_intPtr._hasPalette = true;
do {
@@ -1186,7 +1186,7 @@ void ThreadResource::doRoom() {
}
vm._eventsManager->_intPtr._hasPalette = true;
- vm._graphicsManager->flipPage();
+ vm._screen->flipPage();
vm._eventsManager->sWaitFlip();
} while (!vm.shouldQuit() && !vm._eventsManager->_mouseClicked);
@@ -1234,13 +1234,13 @@ void ThreadResource::doRoom() {
// WORKAROUND: Skipped code from the original, that freed the group,
// reloaded it, and reloaded the cursors
- vm._graphicsManager->_backColors = vm._bVoy->boltEntry(
+ vm._screen->_backColors = vm._bVoy->boltEntry(
vm._playStampGroupId + 1)._cMapResource;
- vm._graphicsManager->_backgroundPage = vm._bVoy->boltEntry(
+ vm._screen->_backgroundPage = vm._bVoy->boltEntry(
vm._playStampGroupId)._picResource;
- vm._graphicsManager->_vPort->setupViewPort();
- vm._graphicsManager->_backColors->startFade();
+ vm._screen->_vPort->setupViewPort();
+ vm._screen->_backColors->startFade();
_vm->flipPageAndWait();
while (!vm.shouldQuit() && (vm._eventsManager->_fadeStatus & 1))
@@ -1265,7 +1265,7 @@ void ThreadResource::doRoom() {
_vm->flipPageAndWait();
- vm._graphicsManager->fadeUpICF1();
+ vm._screen->fadeUpICF1();
voy._eventFlags &= EVTFLAG_RECORDING;
vm._eventsManager->showCursor();
}
@@ -1350,7 +1350,7 @@ int ThreadResource::doInterface() {
_vm->_soundManager->startVOCPlay(fname);
_vm->_eventsManager->getMouseInfo();
- _vm->_graphicsManager->setColor(240, 220, 220, 220);
+ _vm->_screen->setColor(240, 220, 220, 220);
_vm->_eventsManager->_intPtr._hasPalette = true;
_vm->_voy->_eventFlags &= ~EVTFLAG_TIME_DISABLED;
@@ -1424,20 +1424,20 @@ int ThreadResource::doInterface() {
// Regularly update the time display
if (_vm->_voy->_RTANum & 2) {
- _vm->_graphicsManager->drawANumber(_vm->_graphicsManager->_vPort,
+ _vm->_screen->drawANumber(_vm->_screen->_vPort,
_vm->_gameMinute / 10, Common::Point(190, 25));
- _vm->_graphicsManager->drawANumber(_vm->_graphicsManager->_vPort,
+ _vm->_screen->drawANumber(_vm->_screen->_vPort,
_vm->_gameMinute % 10, Common::Point(201, 25));
if (_vm->_voy->_RTANum & 4) {
int v = _vm->_gameHour / 10;
- _vm->_graphicsManager->drawANumber(_vm->_graphicsManager->_vPort,
+ _vm->_screen->drawANumber(_vm->_screen->_vPort,
v == 0 ? 10 : v, Common::Point(161, 25));
- _vm->_graphicsManager->drawANumber(_vm->_graphicsManager->_vPort,
+ _vm->_screen->drawANumber(_vm->_screen->_vPort,
_vm->_gameHour % 10, Common::Point(172, 25));
pic = _vm->_bVoy->boltEntry(_vm->_voy->_isAM ? 272 : 273)._picResource;
- _vm->_graphicsManager->sDrawPic(pic, _vm->_graphicsManager->_vPort,
+ _vm->_screen->sDrawPic(pic, _vm->_screen->_vPort,
Common::Point(215, 27));
}
}
@@ -1605,16 +1605,16 @@ void ThreadResource::loadTheApt() {
_vm->_bVoy->getBoltGroup(_vm->_playStampGroupId);
_vm->_voy->_aptLoadMode = -1;
- _vm->_graphicsManager->_backgroundPage = _vm->_bVoy->boltEntry(
+ _vm->_screen->_backgroundPage = _vm->_bVoy->boltEntry(
_vm->_playStampGroupId + 5)._picResource;
- _vm->_graphicsManager->_vPort->setupViewPort(
- _vm->_graphicsManager->_backgroundPage);
+ _vm->_screen->_vPort->setupViewPort(
+ _vm->_screen->_backgroundPage);
} else {
_vm->_bVoy->getBoltGroup(_vm->_playStampGroupId);
- _vm->_graphicsManager->_backgroundPage = _vm->_bVoy->boltEntry(
+ _vm->_screen->_backgroundPage = _vm->_bVoy->boltEntry(
_vm->_playStampGroupId + 5)._picResource;
- _vm->_graphicsManager->_vPort->setupViewPort(
- _vm->_graphicsManager->_backgroundPage);
+ _vm->_screen->_vPort->setupViewPort(
+ _vm->_screen->_backgroundPage);
}
CMapResource *pal = _vm->_bVoy->boltEntry(_vm->_playStampGroupId + 4)._cMapResource;
@@ -1624,10 +1624,10 @@ void ThreadResource::loadTheApt() {
}
void ThreadResource::freeTheApt() {
- _vm->_graphicsManager->fadeDownICF1(5);
+ _vm->_screen->fadeDownICF1(5);
_vm->flipPageAndWaitForFade();
- _vm->_graphicsManager->fadeUpICF1();
+ _vm->_screen->fadeUpICF1();
if (_vm->_currentVocId != -1) {
_vm->_soundManager->stopVOCPlay();
@@ -1635,17 +1635,17 @@ void ThreadResource::freeTheApt() {
}
if (_vm->_voy->_aptLoadMode == -1) {
- _vm->_graphicsManager->fadeDownICF(6);
+ _vm->_screen->fadeDownICF(6);
} else {
doAptAnim(2);
}
if (_vm->_voy->_aptLoadMode == 140) {
- _vm->_graphicsManager->screenReset();
- _vm->_graphicsManager->resetPalette();
+ _vm->_screen->screenReset();
+ _vm->_screen->resetPalette();
}
- _vm->_graphicsManager->_vPort->setupViewPort(nullptr);
+ _vm->_screen->_vPort->setupViewPort(nullptr);
_vm->_bVoy->freeBoltGroup(_vm->_playStampGroupId);
_vm->_playStampGroupId = -1;
_vm->_voy->_viewBounds = nullptr;
@@ -1705,7 +1705,7 @@ void ThreadResource::doAptAnim(int mode) {
for (int idx = 0; (idx < 6) && !_vm->shouldQuit(); ++idx) {
PictureResource *pic = _vm->_bVoy->boltEntry(id + idx + 1)._picResource;
- _vm->_graphicsManager->_vPort->setupViewPort(pic);
+ _vm->_screen->_vPort->setupViewPort(pic);
pal->startFade();
_vm->flipPageAndWait();
diff --git a/engines/voyeur/module.mk b/engines/voyeur/module.mk
index aab254cf36..a38bdd9ab2 100644
--- a/engines/voyeur/module.mk
+++ b/engines/voyeur/module.mk
@@ -8,7 +8,7 @@ MODULE_OBJS := \
events.o \
files.o \
files_threads.o \
- graphics.o \
+ screen.o \
sound.o \
staticres.o \
voyeur.o \
diff --git a/engines/voyeur/graphics.cpp b/engines/voyeur/screen.cpp
index a20e9f6006..62f609c5c7 100644
--- a/engines/voyeur/graphics.cpp
+++ b/engines/voyeur/screen.cpp
@@ -20,7 +20,7 @@
*
*/
-#include "voyeur/graphics.h"
+#include "voyeur/screen.h"
#include "voyeur/voyeur.h"
#include "voyeur/staticres.h"
#include "engines/util.h"
@@ -38,7 +38,8 @@ DrawInfo::DrawInfo(int penColor, const Common::Point &pos) {
/*------------------------------------------------------------------------*/
-GraphicsManager::GraphicsManager(VoyeurEngine *vm) : _defaultDrawInfo(1, Common::Point()), _drawPtr(&_defaultDrawInfo), _vm(vm) {
+Screen::Screen(VoyeurEngine *vm) : Graphics::Screen(), _vm(vm), _drawPtr(&_defaultDrawInfo),
+ _defaultDrawInfo(1, Common::Point()) {
_SVGAMode = 0;
_planeSelect = 0;
_saveBack = true;
@@ -52,18 +53,17 @@ GraphicsManager::GraphicsManager(VoyeurEngine *vm) : _defaultDrawInfo(1, Common:
_backColors = nullptr;
}
-void GraphicsManager::sInitGraphics() {
+void Screen::sInitGraphics() {
initGraphics(SCREEN_WIDTH, SCREEN_HEIGHT, false);
- _screenSurface.create(SCREEN_WIDTH, SCREEN_HEIGHT, Graphics::PixelFormat::createFormatCLUT8());
+ create(SCREEN_WIDTH, SCREEN_HEIGHT);
clearPalette();
}
-GraphicsManager::~GraphicsManager() {
- _screenSurface.free();
+Screen::~Screen() {
delete _fontChar;
}
-void GraphicsManager::setupMCGASaveRect(ViewPortResource *viewPort) {
+void Screen::setupMCGASaveRect(ViewPortResource *viewPort) {
if (viewPort->_activePage) {
viewPort->_activePage->_flags |= DISPFLAG_1;
Common::Rect *clipRect = _clipPtr;
@@ -77,7 +77,7 @@ void GraphicsManager::setupMCGASaveRect(ViewPortResource *viewPort) {
viewPort->_rectListCount[1] = -1;
}
-void GraphicsManager::addRectOptSaveRect(ViewPortResource *viewPort, int idx, const Common::Rect &bounds) {
+void Screen::addRectOptSaveRect(ViewPortResource *viewPort, int idx, const Common::Rect &bounds) {
if (viewPort->_rectListCount[idx] == -1)
return;
@@ -86,7 +86,7 @@ void GraphicsManager::addRectOptSaveRect(ViewPortResource *viewPort, int idx, co
++viewPort->_rectListCount[idx];
}
-void GraphicsManager::restoreMCGASaveRect(ViewPortResource *viewPort) {
+void Screen::restoreMCGASaveRect(ViewPortResource *viewPort) {
if (viewPort->_rectListCount[0] != -1) {
for (int i = 0; i < viewPort->_rectListCount[0]; ++i) {
addRectOptSaveRect(viewPort, 1, (*viewPort->_rectListPtr[0])[i]);
@@ -106,11 +106,11 @@ void GraphicsManager::restoreMCGASaveRect(ViewPortResource *viewPort) {
viewPort->_rectListCount[1] = count;
}
-void GraphicsManager::addRectNoSaveBack(ViewPortResource *viewPort, int idx, const Common::Rect &bounds) {
+void Screen::addRectNoSaveBack(ViewPortResource *viewPort, int idx, const Common::Rect &bounds) {
// Stubbed/dummy method in the original.
}
-void GraphicsManager::sDrawPic(DisplayResource *srcDisplay, DisplayResource *destDisplay,
+void Screen::sDrawPic(DisplayResource *srcDisplay, DisplayResource *destDisplay,
const Common::Point &initialOffset) {
int width1, width2;
int widthDiff, widthDiff2;
@@ -128,7 +128,8 @@ void GraphicsManager::sDrawPic(DisplayResource *srcDisplay, DisplayResource *des
int runLength;
byte *srcImgData, *destImgData;
- byte *srcP, *destP;
+ const byte *srcP;
+ byte *destP;
byte byteVal, byteVal2;
PictureResource *srcPic;
@@ -292,7 +293,7 @@ void GraphicsManager::sDrawPic(DisplayResource *srcDisplay, DisplayResource *des
// loc_2566F
if (srcFlags & DISPFLAG_2) {
// loc_256FA
- srcP = (byte *)_screenSurface.getPixels() + srcOffset;
+ srcP = (const byte *)getPixels() + srcOffset;
for (int yp = 0; yp < height1; ++yp) {
for (int xp = 0; xp < width2; ++xp, ++srcP, ++destP) {
@@ -325,13 +326,16 @@ void GraphicsManager::sDrawPic(DisplayResource *srcDisplay, DisplayResource *des
}
} else {
// loc_25829
- destP = (byte *)_screenSurface.getPixels() + screenOffset;
+ destP = (byte *)getPixels() + screenOffset;
for (int yp = 0; yp < height1; ++yp) {
Common::copy(srcP, srcP + width2, destP);
srcP += width2 + widthDiff;
destP += width2 + widthDiff2;
}
+
+ addDirtyRect(Common::Rect(offset.x, offset.y, offset.x + width2,
+ offset.y + height1));
}
}
} else {
@@ -341,13 +345,16 @@ void GraphicsManager::sDrawPic(DisplayResource *srcDisplay, DisplayResource *des
error("TODO: sDrawPic variation");
} else {
// loc_2606D
- destP = (byte *)_screenSurface.getPixels() + screenOffset;
+ destP = (byte *)getPixels() + screenOffset;
for (int yp = 0; yp < height1; ++yp) {
Common::copy(srcP, srcP + width2, destP);
destP += width2 + widthDiff2;
srcP += width2 + widthDiff;
}
+
+ addDirtyRect(Common::Rect(offset.x, offset.y, offset.x + width2,
+ offset.y + height1));
}
}
} else {
@@ -530,11 +537,14 @@ void GraphicsManager::sDrawPic(DisplayResource *srcDisplay, DisplayResource *des
// loc_27477
if (destFlags & DISPFLAG_8) {
// loc_27481
- destP = (byte *)_screenSurface.getPixels() + screenOffset;
+ destP = (byte *)getPixels() + screenOffset;
for (int yp = 0; yp < height1; ++yp) {
Common::fill(destP, destP + width2, onOff);
destP += width2 + widthDiff2;
}
+
+ addDirtyRect(Common::Rect(offset.x, offset.y, offset.x + width2,
+ offset.y + height1));
} else {
// loc_2753C
destP = destImgData + screenOffset;
@@ -561,7 +571,7 @@ void GraphicsManager::sDrawPic(DisplayResource *srcDisplay, DisplayResource *des
if (srcFlags & PICFLAG_100) {
if (isClipped) {
// loc_266E3
- destP = (byte *)_screenSurface.getPixels() + screenOffset;
+ destP = (byte *)getPixels() + screenOffset;
tmpWidth = (tmpWidth < 0) ? -tmpWidth : 0;
int xMax = tmpWidth + width2;
tmpHeight = (tmpHeight < 0) ? -tmpHeight : 0;
@@ -592,9 +602,12 @@ void GraphicsManager::sDrawPic(DisplayResource *srcDisplay, DisplayResource *des
if (yp >= tmpHeight)
destP += widthDiff2;
}
+
+ addDirtyRect(Common::Rect(offset.x, offset.y, offset.x + width2,
+ offset.y + height1));
} else {
// loc_26815
- destP = (byte *)_screenSurface.getPixels() + screenOffset;
+ destP = (byte *)getPixels() + screenOffset;
for (int yp = 0; yp < height1; ++yp) {
for (int xi = 0; xi < width2; ++xi, ++destP) {
@@ -618,10 +631,13 @@ void GraphicsManager::sDrawPic(DisplayResource *srcDisplay, DisplayResource *des
destP += widthDiff2;
}
+
+ addDirtyRect(Common::Rect(offset.x, offset.y, offset.x + width2,
+ offset.y + height1));
}
} else {
// Direct screen write
- destP = (byte *)_screenSurface.getPixels() + screenOffset;
+ destP = (byte *)getPixels() + screenOffset;
for (int yp = 0; yp < height1; ++yp) {
for (int xp = 0; xp < width2; ++xp, ++srcP, ++destP) {
@@ -631,6 +647,9 @@ void GraphicsManager::sDrawPic(DisplayResource *srcDisplay, DisplayResource *des
destP += widthDiff2;
srcP += widthDiff;
}
+
+ addDirtyRect(Common::Rect(offset.x, offset.y, offset.x + width2,
+ offset.y + height1));
}
} else if (srcFlags & PICFLAG_100) {
srcP = srcImgData;
@@ -663,7 +682,7 @@ void GraphicsManager::sDrawPic(DisplayResource *srcDisplay, DisplayResource *des
}
} else {
// loc_26BD5
- destP = (byte *)_screenSurface.getPixels() + screenOffset;
+ destP = (byte *)getPixels() + screenOffset;
for (int yp = 0; yp < height1; ++yp) {
byteVal2 = 0;
@@ -684,10 +703,13 @@ void GraphicsManager::sDrawPic(DisplayResource *srcDisplay, DisplayResource *des
destP += widthDiff2;
}
+
+ addDirtyRect(Common::Rect(offset.x, offset.y, offset.x + width2,
+ offset.y + height1));
}
} else {
// loc_26C9A
- destP = (byte *)_screenSurface.getPixels() + screenOffset;
+ destP = (byte *)getPixels() + screenOffset;
for (int yp = 0; yp < height1; ++yp) {
for (int xp = 0; xp < width2; ++xp, ++srcP, ++destP) {
@@ -696,6 +718,9 @@ void GraphicsManager::sDrawPic(DisplayResource *srcDisplay, DisplayResource *des
destP += widthDiff2;
srcP += widthDiff;
}
+
+ addDirtyRect(Common::Rect(offset.x, offset.y, offset.x + width2,
+ offset.y + height1));
}
} else {
// loc_26D2F
@@ -850,12 +875,12 @@ void GraphicsManager::sDrawPic(DisplayResource *srcDisplay, DisplayResource *des
}
}
-void GraphicsManager::drawANumber(DisplayResource *display, int num, const Common::Point &pt) {
+void Screen::drawANumber(DisplayResource *display, int num, const Common::Point &pt) {
PictureResource *pic = _vm->_bVoy->boltEntry(num + 261)._picResource;
sDrawPic(pic, display, pt);
}
-void GraphicsManager::fillPic(DisplayResource *display, byte onOff) {
+void Screen::fillPic(DisplayResource *display, byte onOff) {
PictureResource *pic;
if (display->_flags & DISPFLAG_VIEWPORT) {
pic = ((ViewPortResource *)display)->_currentPic;
@@ -876,11 +901,11 @@ void GraphicsManager::fillPic(DisplayResource *display, byte onOff) {
/**
* Queues the given picture for display
*/
-void GraphicsManager::sDisplayPic(PictureResource *pic) {
+void Screen::sDisplayPic(PictureResource *pic) {
_vm->_eventsManager->_intPtr._flipWait = true;
}
-void GraphicsManager::flipPage() {
+void Screen::flipPage() {
Common::Array<ViewPortResource *> &viewPorts = _viewPortListPtr->_entries;
bool flipFlag = false;
@@ -907,7 +932,7 @@ void GraphicsManager::flipPage() {
}
}
-void GraphicsManager::restoreBack(Common::Array<Common::Rect> &rectList, int rectListCount,
+void Screen::restoreBack(Common::Array<Common::Rect> &rectList, int rectListCount,
PictureResource *srcPic, PictureResource *destPic) {
// WORKAROUND: Since _backgroundPage can point to a resource freed at the end of display methods,
// I'm now explicitly resetting it to null in screenReset(), so at this point it can be null
@@ -929,33 +954,26 @@ void GraphicsManager::restoreBack(Common::Array<Common::Rect> &rectList, int rec
_saveBack = saveBack;
}
-void GraphicsManager::clearPalette() {
- byte palette[768];
- Common::fill(&palette[0], &palette[768], 0);
- g_system->getPaletteManager()->setPalette(&palette[0], 0, 256);
-}
-
-void GraphicsManager::setPalette(const byte *palette, int start, int count) {
- g_system->getPaletteManager()->setPalette(palette, start, count);
+void Screen::setPalette(const byte *palette, int start, int count) {
+ Graphics::Screen::setPalette(palette, start, count);
_vm->_eventsManager->_gameData._hasPalette = false;
}
-void GraphicsManager::setPalette128(const byte *palette, int start, int count) {
+void Screen::setPalette128(const byte *palette, int start, int count) {
byte rgb[3];
- g_system->getPaletteManager()->grabPalette(&rgb[0], 128, 1);
- g_system->getPaletteManager()->setPalette(palette, start, count);
- g_system->getPaletteManager()->setPalette(&rgb[0], 128, 1);
+ getPalette(&rgb[0], 128, 1);
+ Graphics::Screen::setPalette(palette, start, count);
+ Graphics::Screen::setPalette(&rgb[0], 128, 1);
}
-
-void GraphicsManager::resetPalette() {
+void Screen::resetPalette() {
for (int i = 0; i < 256; ++i)
setColor(i, 0, 0, 0);
_vm->_eventsManager->_intPtr._hasPalette = true;
}
-void GraphicsManager::setColor(int idx, byte r, byte g, byte b) {
+void Screen::setColor(int idx, byte r, byte g, byte b) {
byte *vgaP = &_VGAColors[idx * 3];
vgaP[0] = r;
vgaP[1] = g;
@@ -965,7 +983,7 @@ void GraphicsManager::setColor(int idx, byte r, byte g, byte b) {
_vm->_eventsManager->_intPtr._palEndIndex = MAX(_vm->_eventsManager->_intPtr._palEndIndex, idx);
}
-void GraphicsManager::setOneColor(int idx, byte r, byte g, byte b) {
+void Screen::setOneColor(int idx, byte r, byte g, byte b) {
byte palEntry[3];
palEntry[0] = r;
palEntry[1] = g;
@@ -973,7 +991,7 @@ void GraphicsManager::setOneColor(int idx, byte r, byte g, byte b) {
g_system->getPaletteManager()->setPalette(&palEntry[0], idx, 1);
}
-void GraphicsManager::setColors(int start, int count, const byte *pal) {
+void Screen::setColors(int start, int count, const byte *pal) {
for (int i = 0; i < count; ++i) {
if ((i + start) != 128) {
const byte *rgb = pal + i * 3;
@@ -984,7 +1002,7 @@ void GraphicsManager::setColors(int start, int count, const byte *pal) {
_vm->_eventsManager->_intPtr._hasPalette = true;
}
-void GraphicsManager::screenReset() {
+void Screen::screenReset() {
resetPalette();
_backgroundPage = NULL;
@@ -994,7 +1012,7 @@ void GraphicsManager::screenReset() {
_vm->flipPageAndWait();
}
-void GraphicsManager::fadeDownICF1(int steps) {
+void Screen::fadeDownICF1(int steps) {
if (steps > 0) {
int stepAmount = _vm->_voy->_fadingAmount2 / steps;
@@ -1007,7 +1025,7 @@ void GraphicsManager::fadeDownICF1(int steps) {
_vm->_voy->_fadingAmount2 = 0;
}
-void GraphicsManager::fadeUpICF1(int steps) {
+void Screen::fadeUpICF1(int steps) {
if (steps > 0) {
int stepAmount = (63 - _vm->_voy->_fadingAmount2) / steps;
@@ -1020,7 +1038,7 @@ void GraphicsManager::fadeUpICF1(int steps) {
_vm->_voy->_fadingAmount2 = 63;
}
-void GraphicsManager::fadeDownICF(int steps) {
+void Screen::fadeDownICF(int steps) {
if (steps > 0) {
_vm->_eventsManager->hideCursor();
int stepAmount1 = _vm->_voy->_fadingAmount1 / steps;
@@ -1037,14 +1055,19 @@ void GraphicsManager::fadeDownICF(int steps) {
_vm->_voy->_fadingAmount2 = 0;
}
-void GraphicsManager::drawDot() {
- for (int y = 0; y < 9; ++y) {
- byte *pDest = (byte *)_screenSurface.getPixels() + DOT_LINE_START[y] + DOT_LINE_OFFSET[y];
- Common::fill(pDest, pDest + DOT_LINE_LENGTH[y], 0x80);
+void Screen::drawDot() {
+ for (int idx = 0; idx < 9; ++idx) {
+ uint offset = DOT_LINE_START[idx] + DOT_LINE_OFFSET[idx];
+ int xp = offset % SCREEN_WIDTH;
+ int yp = offset / SCREEN_WIDTH;
+
+ byte *pDest = (byte *)getPixels() + offset;
+ Common::fill(pDest, pDest + DOT_LINE_LENGTH[idx], 0x80);
+ addDirtyRect(Common::Rect(xp, yp, xp + DOT_LINE_LENGTH[idx], yp + 1));
}
}
-void GraphicsManager::synchronize(Common::Serializer &s) {
+void Screen::synchronize(Common::Serializer &s) {
s.syncBytes(&_VGAColors[0], PALETTE_SIZE);
}
diff --git a/engines/voyeur/graphics.h b/engines/voyeur/screen.h
index e4d0b38650..aaf61747a4 100644
--- a/engines/voyeur/graphics.h
+++ b/engines/voyeur/screen.h
@@ -27,17 +27,15 @@
#include "common/array.h"
#include "common/rect.h"
#include "common/serializer.h"
-#include "graphics/surface.h"
+#include "graphics/screen.h"
namespace Voyeur {
#define SCREEN_WIDTH 320
#define SCREEN_HEIGHT 200
-#define PALETTE_COUNT 256
-#define PALETTE_SIZE (256 * 3)
class VoyeurEngine;
-class GraphicsManager;
+class Screen;
class DisplayResource;
class PictureResource;
class ViewPortResource;
@@ -54,12 +52,12 @@ public:
DrawInfo(int penColor, const Common::Point &pos);
};
-typedef void (GraphicsManager::*GraphicMethodPtr)();
-typedef void (GraphicsManager::*ViewPortSetupPtr)(ViewPortResource *);
-typedef void (GraphicsManager::*ViewPortAddPtr)(ViewPortResource *, int idx, const Common::Rect &bounds);
-typedef void (GraphicsManager::*ViewPortRestorePtr)(ViewPortResource *);
+typedef void (Screen::*ScreenMethodPtr)();
+typedef void (Screen::*ViewPortSetupPtr)(ViewPortResource *);
+typedef void (Screen::*ViewPortAddPtr)(ViewPortResource *, int idx, const Common::Rect &bounds);
+typedef void (Screen::*ViewPortRestorePtr)(ViewPortResource *);
-class GraphicsManager {
+class Screen: public Graphics::Screen {
public:
byte _VGAColors[PALETTE_SIZE];
PictureResource *_backgroundPage;
@@ -69,7 +67,6 @@ public:
bool _saveBack;
Common::Rect *_clipPtr;
uint _planeSelect;
- Graphics::Surface _screenSurface;
CMapResource *_backColors;
FontInfoResource *_fontPtr;
PictureResource *_fontChar;
@@ -81,8 +78,8 @@ private:
void restoreBack(Common::Array<Common::Rect> &rectList, int rectListCount,
PictureResource *srcPic, PictureResource *destPic);
public:
- GraphicsManager(VoyeurEngine *vm);
- ~GraphicsManager();
+ Screen(VoyeurEngine *vm);
+ virtual ~Screen();
void sInitGraphics();
@@ -96,7 +93,6 @@ public:
void sDisplayPic(PictureResource *pic);
void drawANumber(DisplayResource *display, int num, const Common::Point &pt);
void flipPage();
- void clearPalette();
void setPalette(const byte *palette, int start, int count);
void setPalette128(const byte *palette, int start, int count);
void resetPalette();
diff --git a/engines/voyeur/voyeur.cpp b/engines/voyeur/voyeur.cpp
index cbb6846340..01b76a72d1 100644
--- a/engines/voyeur/voyeur.cpp
+++ b/engines/voyeur/voyeur.cpp
@@ -22,7 +22,7 @@
#include "voyeur/voyeur.h"
#include "voyeur/animation.h"
-#include "voyeur/graphics.h"
+#include "voyeur/screen.h"
#include "voyeur/staticres.h"
#include "common/scummsys.h"
#include "common/config-manager.h"
@@ -40,7 +40,7 @@ VoyeurEngine::VoyeurEngine(OSystem *syst, const VoyeurGameDescription *gameDesc)
_debugger = nullptr;
_eventsManager = nullptr;
_filesManager = nullptr;
- _graphicsManager = nullptr;
+ _screen = nullptr;
_soundManager = nullptr;
_voy = nullptr;
_bVoy = NULL;
@@ -65,13 +65,6 @@ VoyeurEngine::VoyeurEngine(OSystem *syst, const VoyeurGameDescription *gameDesc)
DebugMan.addDebugChannel(kDebugScripts, "scripts", "Game scripts");
- _debugger = new Debugger(this);
- _eventsManager = new EventsManager(this);
- _filesManager = new FilesManager(this);
- _graphicsManager = new GraphicsManager(this);
- _soundManager = new SoundManager(_mixer);
- _voy = new SVoy(this);
-
_stampLibPtr = nullptr;
_controlGroupPtr = nullptr;
_stampData = nullptr;
@@ -88,7 +81,7 @@ VoyeurEngine::~VoyeurEngine() {
delete _bVoy;
delete _voy;
delete _soundManager;
- delete _graphicsManager;
+ delete _screen;
delete _filesManager;
delete _eventsManager;
delete _debugger;
@@ -126,15 +119,22 @@ void VoyeurEngine::ESP_Init() {
}
void VoyeurEngine::globalInitBolt() {
+ _debugger = new Debugger(this);
+ _eventsManager = new EventsManager(this);
+ _filesManager = new FilesManager(this);
+ _screen = new Screen(this);
+ _soundManager = new SoundManager(_mixer);
+ _voy = new SVoy(this);
+
initBolt();
_filesManager->openBoltLib("bvoy.blt", _bVoy);
_bVoy->getBoltGroup(0x000);
_bVoy->getBoltGroup(0x100);
- _graphicsManager->_fontPtr = &_defaultFontInfo;
- _graphicsManager->_fontPtr->_curFont = _bVoy->boltEntry(0x101)._fontResource;
- assert(_graphicsManager->_fontPtr->_curFont);
+ _screen->_fontPtr = &_defaultFontInfo;
+ _screen->_fontPtr->_curFont = _bVoy->boltEntry(0x101)._fontResource;
+ assert(_screen->_fontPtr->_curFont);
// Setup default flags
_voy->_viewBounds = nullptr;
@@ -144,13 +144,13 @@ void VoyeurEngine::globalInitBolt() {
void VoyeurEngine::initBolt() {
vInitInterrupts();
- _graphicsManager->sInitGraphics();
+ _screen->sInitGraphics();
_eventsManager->vInitColor();
initInput();
}
void VoyeurEngine::vInitInterrupts() {
- _eventsManager->_intPtr._palette = &_graphicsManager->_VGAColors[0];
+ _eventsManager->_intPtr._palette = &_screen->_VGAColors[0];
}
void VoyeurEngine::initInput() {
@@ -213,8 +213,8 @@ bool VoyeurEngine::doHeadTitle() {
}
void VoyeurEngine::showConversionScreen() {
- _graphicsManager->_backgroundPage = _bVoy->boltEntry(0x502)._picResource;
- _graphicsManager->_vPort->setupViewPort();
+ _screen->_backgroundPage = _bVoy->boltEntry(0x502)._picResource;
+ _screen->_vPort->setupViewPort();
flipPageAndWait();
// Immediate palette load to show the initial screen
@@ -237,7 +237,7 @@ void VoyeurEngine::showConversionScreen() {
flipPageAndWaitForFade();
- _graphicsManager->screenReset();
+ _screen->screenReset();
}
bool VoyeurEngine::doLock() {
@@ -249,28 +249,28 @@ bool VoyeurEngine::doLock() {
if (_bVoy->getBoltGroup(0x700)) {
Common::String password = "3333";
- _graphicsManager->_backgroundPage = _bVoy->getPictureResource(0x700);
- _graphicsManager->_backColors = _bVoy->getCMapResource(0x701);
+ _screen->_backgroundPage = _bVoy->getPictureResource(0x700);
+ _screen->_backColors = _bVoy->getCMapResource(0x701);
PictureResource *cursorPic = _bVoy->getPictureResource(0x702);
_voy->_viewBounds = _bVoy->boltEntry(0x704)._rectResource;
Common::Array<RectEntry> &hotspots = _bVoy->boltEntry(0x705)._rectResource->_entries;
assert(cursorPic);
- _graphicsManager->_vPort->setupViewPort();
+ _screen->_vPort->setupViewPort();
- _graphicsManager->_backColors->startFade();
- _graphicsManager->_vPort->_parent->_flags |= DISPFLAG_8;
- _graphicsManager->flipPage();
+ _screen->_backColors->startFade();
+ _screen->_vPort->_parent->_flags |= DISPFLAG_8;
+ _screen->flipPage();
_eventsManager->sWaitFlip();
while (!shouldQuit() && (_eventsManager->_fadeStatus & 1))
_eventsManager->delay(1);
_eventsManager->setCursorColor(127, 0);
- _graphicsManager->setColor(1, 64, 64, 64);
- _graphicsManager->setColor(2, 96, 96, 96);
- _graphicsManager->setColor(3, 160, 160, 160);
- _graphicsManager->setColor(4, 224, 224, 224);
+ _screen->setColor(1, 64, 64, 64);
+ _screen->setColor(2, 96, 96, 96);
+ _screen->setColor(3, 160, 160, 160);
+ _screen->setColor(4, 224, 224, 224);
// Set up the cursor
_eventsManager->setCursor(cursorPic);
@@ -278,9 +278,9 @@ bool VoyeurEngine::doLock() {
_eventsManager->_intPtr._hasPalette = true;
- _graphicsManager->_fontPtr->_curFont = _bVoy->boltEntry(0x708)._fontResource;
- _graphicsManager->_fontPtr->_fontSaveBack = 0;
- _graphicsManager->_fontPtr->_fontFlags = DISPFLAG_NONE;
+ _screen->_fontPtr->_curFont = _bVoy->boltEntry(0x708)._fontResource;
+ _screen->_fontPtr->_fontSaveBack = 0;
+ _screen->_fontPtr->_fontFlags = DISPFLAG_NONE;
Common::String dateString = "ScummVM";
Common::String displayString = Common::String::format("Last Play %s", dateString.c_str());
@@ -288,16 +288,16 @@ bool VoyeurEngine::doLock() {
bool firstLoop = true;
bool breakFlag = false;
while (!breakFlag && !shouldQuit()) {
- _graphicsManager->_vPort->setupViewPort();
+ _screen->_vPort->setupViewPort();
flipPageAndWait();
// Display the last play time
- _graphicsManager->_fontPtr->_pos = Common::Point(0, 97);
- _graphicsManager->_fontPtr->_justify = ALIGN_CENTER;
- _graphicsManager->_fontPtr->_justifyWidth = 384;
- _graphicsManager->_fontPtr->_justifyHeight = 97;
+ _screen->_fontPtr->_pos = Common::Point(0, 97);
+ _screen->_fontPtr->_justify = ALIGN_CENTER;
+ _screen->_fontPtr->_justifyWidth = 384;
+ _screen->_fontPtr->_justifyHeight = 97;
- _graphicsManager->_vPort->drawText(displayString);
+ _screen->_vPort->drawText(displayString);
flipPageAndWait();
if (firstLoop) {
@@ -356,7 +356,7 @@ bool VoyeurEngine::doLock() {
} else if (key == 11) {
// New code
if ((password.empty() && displayString.empty()) || (password != displayString)) {
- _graphicsManager->_vPort->setupViewPort();
+ _screen->_vPort->setupViewPort();
password = displayString;
displayString = "";
continue;
@@ -373,9 +373,9 @@ bool VoyeurEngine::doLock() {
_soundManager->playVOCMap(wrongVoc, wrongVocSize);
}
- _graphicsManager->fillPic(_graphicsManager->_vPort, 0);
+ _screen->fillPic(_screen->_vPort, 0);
flipPageAndWait();
- _graphicsManager->resetPalette();
+ _screen->resetPalette();
_voy->_viewBounds = nullptr;
_bVoy->freeBoltGroup(0x700);
@@ -393,9 +393,9 @@ void VoyeurEngine::showTitleScreen() {
if (!_bVoy->getBoltGroup(0x500))
return;
- _graphicsManager->_backgroundPage = _bVoy->getPictureResource(0x500);
+ _screen->_backgroundPage = _bVoy->getPictureResource(0x500);
- _graphicsManager->_vPort->setupViewPort();
+ _screen->_vPort->setupViewPort();
flipPageAndWait();
// Immediate palette load to show the initial screen
@@ -422,18 +422,18 @@ void VoyeurEngine::showTitleScreen() {
return;
}
- _graphicsManager->screenReset();
+ _screen->screenReset();
_eventsManager->delayClick(200);
// Voyeur title
playRL2Video("a1100100.rl2");
- _graphicsManager->screenReset();
+ _screen->screenReset();
_bVoy->freeBoltGroup(0x500);
}
void VoyeurEngine::doOpening() {
- _graphicsManager->screenReset();
+ _screen->screenReset();
if (!_bVoy->getBoltGroup(0x200))
return;
@@ -459,10 +459,10 @@ void VoyeurEngine::doOpening() {
_voy->_eventFlags &= ~EVTFLAG_TIME_DISABLED;
for (int i = 0; i < 256; ++i)
- _graphicsManager->setColor(i, 8, 8, 8);
+ _screen->setColor(i, 8, 8, 8);
_eventsManager->_intPtr._hasPalette = true;
- _graphicsManager->_vPort->setupViewPort();
+ _screen->_vPort->setupViewPort();
flipPageAndWait();
RL2Decoder decoder;
@@ -472,14 +472,12 @@ void VoyeurEngine::doOpening() {
while (!shouldQuit() && !decoder.endOfVideo() && !_eventsManager->_mouseClicked) {
if (decoder.hasDirtyPalette()) {
const byte *palette = decoder.getPalette();
- _graphicsManager->setPalette(palette, 0, 256);
+ _screen->setPalette(palette, 0, 256);
}
if (decoder.needsUpdate()) {
const Graphics::Surface *frame = decoder.decodeNextFrame();
-
- Common::copy((const byte *)frame->getPixels(), (const byte *)frame->getPixels() + 320 * 200,
- (byte *)_graphicsManager->_screenSurface.getPixels());
+ _screen->blitFrom(*frame);
if (decoder.getCurFrame() >= (int32)READ_LE_UINT32(frameTable + frameIndex * 4)) {
if (creditShow) {
@@ -499,7 +497,7 @@ void VoyeurEngine::doOpening() {
}
if (textPic) {
- _graphicsManager->sDrawPic(textPic, _graphicsManager->_vPort, textPos);
+ _screen->sDrawPic(textPic, _screen->_vPort, textPos);
}
flipPageAndWait();
@@ -527,14 +525,12 @@ void VoyeurEngine::playRL2Video(const Common::String &filename) {
while (!shouldQuit() && !decoder.endOfVideo() && !_eventsManager->_mouseClicked) {
if (decoder.hasDirtyPalette()) {
const byte *palette = decoder.getPalette();
- _graphicsManager->setPalette(palette, 0, 256);
+ _screen->setPalette(palette, 0, 256);
}
if (decoder.needsUpdate()) {
const Graphics::Surface *frame = decoder.decodeNextFrame();
-
- Common::copy((const byte *)frame->getPixels(), (const byte *)frame->getPixels() + 320 * 200,
- (byte *)_graphicsManager->_screenSurface.getPixels());
+ _screen->blitFrom(*frame);
}
_eventsManager->getMouseInfo();
@@ -573,17 +569,16 @@ void VoyeurEngine::playAVideoDuration(int videoId, int duration) {
(decoder.getCurFrame() < endFrame)) {
if (decoder.needsUpdate()) {
const Graphics::Surface *frame = decoder.decodeNextFrame();
+ _screen->blitFrom(*frame);
- Common::copy((const byte *)frame->getPixels(), (const byte *)frame->getPixels() + 320 * 200,
- (byte *)_graphicsManager->_screenSurface.getPixels());
if (_voy->_eventFlags & EVTFLAG_RECORDING)
- _graphicsManager->drawDot();
+ _screen->drawDot();
}
if (decoder.hasDirtyPalette()) {
const byte *palette = decoder.getPalette();
- _graphicsManager->setPalette(palette, 0, decoder.getPaletteCount());
- _graphicsManager->setOneColor(128, 220, 20, 20);
+ _screen->setPalette(palette, 0, decoder.getPaletteCount());
+ _screen->setOneColor(128, 220, 20, 20);
}
_eventsManager->getMouseInfo();
@@ -591,13 +586,13 @@ void VoyeurEngine::playAVideoDuration(int videoId, int duration) {
}
// RL2 finished
- _graphicsManager->screenReset();
+ _screen->screenReset();
_voy->_eventFlags &= ~EVTFLAG_RECORDING;
if (_voy->_eventFlags & EVTFLAG_8) {
assert(pic);
- byte *imgData = _graphicsManager->_vPort->_currentPic->_imgData;
- _graphicsManager->_vPort->_currentPic->_imgData = pic->_imgData;
+ byte *imgData = _screen->_vPort->_currentPic->_imgData;
+ _screen->_vPort->_currentPic->_imgData = pic->_imgData;
pic->_imgData = imgData;
_voy->_eventFlags &= ~EVTFLAG_8;
}
@@ -608,13 +603,13 @@ void VoyeurEngine::playAVideoDuration(int videoId, int duration) {
void VoyeurEngine::playAudio(int audioId) {
_bVoy->getBoltGroup(0x7F00);
- _graphicsManager->_backgroundPage = _bVoy->boltEntry(0x7F00 +
+ _screen->_backgroundPage = _bVoy->boltEntry(0x7F00 +
BLIND_TABLE[audioId] * 2)._picResource;
- _graphicsManager->_backColors = _bVoy->boltEntry(0x7F01 +
+ _screen->_backColors = _bVoy->boltEntry(0x7F01 +
BLIND_TABLE[audioId] * 2)._cMapResource;
- _graphicsManager->_vPort->setupViewPort();
- _graphicsManager->_backColors->startFade();
+ _screen->_vPort->setupViewPort();
+ _screen->_backColors->startFade();
flipPageAndWaitForFade();
_voy->_eventFlags &= ~EVTFLAG_TIME_DISABLED;
@@ -633,26 +628,26 @@ void VoyeurEngine::playAudio(int audioId) {
_soundManager->stopVOCPlay();
_bVoy->freeBoltGroup(0x7F00);
- _graphicsManager->_vPort->setupViewPort(NULL);
+ _screen->_vPort->setupViewPort(NULL);
_voy->_eventFlags &= ~EVTFLAG_RECORDING;
_voy->_playStampMode = 129;
}
void VoyeurEngine::doTransitionCard(const Common::String &time, const Common::String &location) {
- _graphicsManager->setColor(128, 16, 16, 16);
- _graphicsManager->setColor(224, 220, 220, 220);
+ _screen->setColor(128, 16, 16, 16);
+ _screen->setColor(224, 220, 220, 220);
_eventsManager->_intPtr._hasPalette = true;
- _graphicsManager->_vPort->setupViewPort(NULL);
- _graphicsManager->_vPort->fillPic(0x80);
- _graphicsManager->flipPage();
+ _screen->_vPort->setupViewPort(NULL);
+ _screen->_vPort->fillPic(0x80);
+ _screen->flipPage();
_eventsManager->sWaitFlip();
flipPageAndWait();
- _graphicsManager->_vPort->fillPic(0x80);
+ _screen->_vPort->fillPic(0x80);
- FontInfoResource &fi = *_graphicsManager->_fontPtr;
+ FontInfoResource &fi = *_screen->_fontPtr;
fi._curFont = _bVoy->boltEntry(257)._fontResource;
fi._foreColor = 224;
fi._fontSaveBack = 0;
@@ -661,7 +656,7 @@ void VoyeurEngine::doTransitionCard(const Common::String &time, const Common::St
fi._justifyWidth = 384;
fi._justifyHeight = 120;
- _graphicsManager->_vPort->drawText(time);
+ _screen->_vPort->drawText(time);
if (!location.empty()) {
fi._pos = Common::Point(0, 138);
@@ -669,7 +664,7 @@ void VoyeurEngine::doTransitionCard(const Common::String &time, const Common::St
fi._justifyWidth = 384;
fi._justifyHeight = 140;
- _graphicsManager->_vPort->drawText(location);
+ _screen->_vPort->drawText(location);
}
flipPageAndWait();
@@ -680,8 +675,8 @@ void VoyeurEngine::saveLastInplay() {
}
void VoyeurEngine::flipPageAndWait() {
- _graphicsManager->_vPort->_flags |= DISPFLAG_8;
- _graphicsManager->flipPage();
+ _screen->_vPort->_flags |= DISPFLAG_8;
+ _screen->flipPage();
_eventsManager->sWaitFlip();
}
@@ -702,7 +697,7 @@ void VoyeurEngine::showEndingNews() {
PictureResource *pic = _bVoy->boltEntry(_playStampGroupId)._picResource;
CMapResource *pal = _bVoy->boltEntry(_playStampGroupId + 1)._cMapResource;
- _graphicsManager->_vPort->setupViewPort(pic);
+ _screen->_vPort->setupViewPort(pic);
pal->startFade();
flipPageAndWaitForFade();
@@ -717,7 +712,7 @@ void VoyeurEngine::showEndingNews() {
pal = _bVoy->boltEntry(_playStampGroupId + idx * 2 + 1)._cMapResource;
}
- _graphicsManager->_vPort->setupViewPort(pic);
+ _screen->_vPort->setupViewPort(pic);
pal->startFade();
flipPageAndWaitForFade();
@@ -852,7 +847,7 @@ void VoyeurEngine::synchronize(Common::Serializer &s) {
// Sub-systems
_voy->synchronize(s);
- _graphicsManager->synchronize(s);
+ _screen->synchronize(s);
_mainThread->synchronize(s);
_controlPtr->_state->synchronize(s);
}
@@ -906,8 +901,8 @@ void VoyeurSavegameHeader::write(Common::OutSaveFile *f, VoyeurEngine *vm, const
// Create a thumbnail and save it
Graphics::Surface *thumb = new Graphics::Surface();
- ::createThumbnail(thumb, (byte *)vm->_graphicsManager->_screenSurface.getPixels(),
- SCREEN_WIDTH, SCREEN_HEIGHT, vm->_graphicsManager->_VGAColors);
+ ::createThumbnail(thumb, (const byte *)vm->_screen->getPixels(),
+ SCREEN_WIDTH, SCREEN_HEIGHT, vm->_screen->_VGAColors);
Graphics::saveThumbnail(*f, *thumb);
thumb->free();
delete thumb;
diff --git a/engines/voyeur/voyeur.h b/engines/voyeur/voyeur.h
index e0bb734fa8..9cda85fd51 100644
--- a/engines/voyeur/voyeur.h
+++ b/engines/voyeur/voyeur.h
@@ -27,7 +27,7 @@
#include "voyeur/data.h"
#include "voyeur/events.h"
#include "voyeur/files.h"
-#include "voyeur/graphics.h"
+#include "voyeur/screen.h"
#include "voyeur/sound.h"
#include "common/scummsys.h"
#include "common/system.h"
@@ -164,7 +164,7 @@ public:
Debugger *_debugger;
EventsManager *_eventsManager;
FilesManager *_filesManager;
- GraphicsManager *_graphicsManager;
+ Screen *_screen;
SoundManager *_soundManager;
SVoy *_voy;
diff --git a/engines/voyeur/voyeur_game.cpp b/engines/voyeur/voyeur_game.cpp
index 13ef31839a..e9591955fc 100644
--- a/engines/voyeur/voyeur_game.cpp
+++ b/engines/voyeur/voyeur_game.cpp
@@ -149,8 +149,8 @@ void VoyeurEngine::playStamp() {
case 130: {
// user selected to send the tape
if (_bVoy->getBoltGroup(_playStampGroupId)) {
- _graphicsManager->_backgroundPage = _bVoy->boltEntry(_playStampGroupId)._picResource;
- _graphicsManager->_backColors = _bVoy->boltEntry(_playStampGroupId + 1)._cMapResource;
+ _screen->_backgroundPage = _bVoy->boltEntry(_playStampGroupId)._picResource;
+ _screen->_backColors = _bVoy->boltEntry(_playStampGroupId + 1)._cMapResource;
buttonId = getChooseButton();
if (_eventsManager->_rightClick)
@@ -158,7 +158,7 @@ void VoyeurEngine::playStamp() {
buttonId = 4;
_bVoy->freeBoltGroup(_playStampGroupId);
- _graphicsManager->screenReset();
+ _screen->screenReset();
_playStampGroupId = -1;
flag = true;
@@ -232,8 +232,8 @@ void VoyeurEngine::closeStamp() {
}
void VoyeurEngine::doTailTitle() {
- _graphicsManager->_vPort->setupViewPort(NULL);
- _graphicsManager->screenReset();
+ _screen->_vPort->setupViewPort(NULL);
+ _screen->screenReset();
if (_bVoy->getBoltGroup(0x600)) {
RL2Decoder decoder;
@@ -245,12 +245,12 @@ void VoyeurEngine::doTailTitle() {
doClosingCredits();
if (!shouldQuit() && !_eventsManager->_mouseClicked) {
- _graphicsManager->screenReset();
+ _screen->screenReset();
PictureResource *pic = _bVoy->boltEntry(0x602)._picResource;
CMapResource *pal = _bVoy->boltEntry(0x603)._cMapResource;
- _graphicsManager->_vPort->setupViewPort(pic);
+ _screen->_vPort->setupViewPort(pic);
pal->startFade();
flipPageAndWaitForFade();
_eventsManager->delayClick(300);
@@ -258,7 +258,7 @@ void VoyeurEngine::doTailTitle() {
pic = _bVoy->boltEntry(0x604)._picResource;
pal = _bVoy->boltEntry(0x605)._cMapResource;
- _graphicsManager->_vPort->setupViewPort(pic);
+ _screen->_vPort->setupViewPort(pic);
pal->startFade();
flipPageAndWaitForFade();
_eventsManager->delayClick(120);
@@ -283,26 +283,26 @@ void VoyeurEngine::doClosingCredits() {
const char *msg = (const char *)_bVoy->memberAddr(0x404);
const byte *creditList = (const byte *)_bVoy->memberAddr(0x405);
- _graphicsManager->_vPort->setupViewPort(NULL);
- _graphicsManager->setColor(1, 180, 180, 180);
- _graphicsManager->setColor(2, 200, 200, 200);
+ _screen->_vPort->setupViewPort(NULL);
+ _screen->setColor(1, 180, 180, 180);
+ _screen->setColor(2, 200, 200, 200);
_eventsManager->_intPtr._hasPalette = true;
- _graphicsManager->_fontPtr->_curFont = _bVoy->boltEntry(0x402)._fontResource;
- _graphicsManager->_fontPtr->_foreColor = 2;
- _graphicsManager->_fontPtr->_backColor = 2;
- _graphicsManager->_fontPtr->_fontSaveBack = false;
- _graphicsManager->_fontPtr->_fontFlags = DISPFLAG_NONE;
+ _screen->_fontPtr->_curFont = _bVoy->boltEntry(0x402)._fontResource;
+ _screen->_fontPtr->_foreColor = 2;
+ _screen->_fontPtr->_backColor = 2;
+ _screen->_fontPtr->_fontSaveBack = false;
+ _screen->_fontPtr->_fontFlags = DISPFLAG_NONE;
_soundManager->startVOCPlay(152);
- FontInfoResource &fi = *_graphicsManager->_fontPtr;
+ FontInfoResource &fi = *_screen->_fontPtr;
for (int idx = 0; idx < 78; ++idx) {
const byte *entry = creditList + idx * 6;
int flags = READ_LE_UINT16(entry + 4);
if (flags & 0x10)
- _graphicsManager->_vPort->fillPic(0);
+ _screen->_vPort->fillPic(0);
if (flags & 1) {
fi._foreColor = 1;
@@ -312,7 +312,7 @@ void VoyeurEngine::doClosingCredits() {
fi._justifyHeight = 240;
fi._pos = Common::Point(0, READ_LE_UINT16(entry));
- _graphicsManager->_vPort->drawText(msg);
+ _screen->_vPort->drawText(msg);
msg += strlen(msg) + 1;
}
@@ -324,7 +324,7 @@ void VoyeurEngine::doClosingCredits() {
fi._justifyHeight = 240;
fi._pos = Common::Point(0, READ_LE_UINT16(entry));
- _graphicsManager->_vPort->drawText(msg);
+ _screen->_vPort->drawText(msg);
msg += strlen(msg) + 1;
}
@@ -336,7 +336,7 @@ void VoyeurEngine::doClosingCredits() {
fi._justifyHeight = 240;
fi._pos = Common::Point(38, READ_LE_UINT16(entry));
- _graphicsManager->_vPort->drawText(msg);
+ _screen->_vPort->drawText(msg);
msg += strlen(msg) + 1;
fi._foreColor = 2;
@@ -345,7 +345,7 @@ void VoyeurEngine::doClosingCredits() {
fi._justifyHeight = 240;
fi._pos = Common::Point(198, READ_LE_UINT16(entry));
- _graphicsManager->_vPort->drawText(msg);
+ _screen->_vPort->drawText(msg);
msg += strlen(msg) + 1;
}
@@ -357,7 +357,7 @@ void VoyeurEngine::doClosingCredits() {
fi._justifyHeight = 240;
fi._pos = Common::Point(0, READ_LE_UINT16(entry));
- _graphicsManager->_vPort->drawText(msg);
+ _screen->_vPort->drawText(msg);
msg += strlen(msg) + 1;
fi._foreColor = 2;
@@ -367,7 +367,7 @@ void VoyeurEngine::doClosingCredits() {
fi._justifyHeight = 240;
fi._pos = Common::Point(0, READ_LE_UINT16(entry) + 13);
- _graphicsManager->_vPort->drawText(msg);
+ _screen->_vPort->drawText(msg);
msg += strlen(msg) + 1;
}
@@ -381,19 +381,19 @@ void VoyeurEngine::doClosingCredits() {
}
_soundManager->stopVOCPlay();
- _graphicsManager->_fontPtr->_curFont = _bVoy->boltEntry(0x101)._fontResource;
+ _screen->_fontPtr->_curFont = _bVoy->boltEntry(0x101)._fontResource;
_bVoy->freeBoltGroup(0x400);
}
void VoyeurEngine::doPiracy() {
- _graphicsManager->screenReset();
- _graphicsManager->setColor(1, 0, 0, 0);
- _graphicsManager->setColor(2, 255, 255, 255);
+ _screen->screenReset();
+ _screen->setColor(1, 0, 0, 0);
+ _screen->setColor(2, 255, 255, 255);
_eventsManager->_intPtr._hasPalette = true;
- _graphicsManager->_vPort->setupViewPort(NULL);
- _graphicsManager->_vPort->fillPic(1);
+ _screen->_vPort->setupViewPort(NULL);
+ _screen->_vPort->fillPic(1);
- FontInfoResource &fi = *_graphicsManager->_fontPtr;
+ FontInfoResource &fi = *_screen->_fontPtr;
fi._curFont = _bVoy->boltEntry(0x101)._fontResource;
fi._foreColor = 2;
fi._backColor = 2;
@@ -406,7 +406,7 @@ void VoyeurEngine::doPiracy() {
// Loop through the piracy message array to draw each line
for (int idx = 0, yp = 33; idx < 10; ++idx) {
fi._pos = Common::Point(0, yp);
- _graphicsManager->_vPort->drawText(PIRACY_MESSAGE[idx]);
+ _screen->_vPort->drawText(PIRACY_MESSAGE[idx]);
yp += fi._curFont->_fontHeight + 4;
}
@@ -439,27 +439,27 @@ void VoyeurEngine::reviewTape() {
_voy->_viewBounds = _bVoy->boltEntry(0x907)._rectResource;
Common::Array<RectEntry> &hotspots = _bVoy->boltEntry(0x906)._rectResource->_entries;
- _graphicsManager->_backColors = _bVoy->boltEntry(0x902)._cMapResource;
- _graphicsManager->_backgroundPage = _bVoy->boltEntry(0x901)._picResource;
- _graphicsManager->_vPort->setupViewPort(_graphicsManager->_backgroundPage);
- _graphicsManager->_backColors->startFade();
+ _screen->_backColors = _bVoy->boltEntry(0x902)._cMapResource;
+ _screen->_backgroundPage = _bVoy->boltEntry(0x901)._picResource;
+ _screen->_vPort->setupViewPort(_screen->_backgroundPage);
+ _screen->_backColors->startFade();
flipPageAndWaitForFade();
- _graphicsManager->setColor(1, 32, 32, 32);
- _graphicsManager->setColor(2, 96, 96, 96);
- _graphicsManager->setColor(3, 160, 160, 160);
- _graphicsManager->setColor(4, 224, 224, 224);
- _graphicsManager->setColor(9, 24, 64, 24);
- _graphicsManager->setColor(10, 64, 132, 64);
- _graphicsManager->setColor(11, 100, 192, 100);
- _graphicsManager->setColor(12, 120, 248, 120);
+ _screen->setColor(1, 32, 32, 32);
+ _screen->setColor(2, 96, 96, 96);
+ _screen->setColor(3, 160, 160, 160);
+ _screen->setColor(4, 224, 224, 224);
+ _screen->setColor(9, 24, 64, 24);
+ _screen->setColor(10, 64, 132, 64);
+ _screen->setColor(11, 100, 192, 100);
+ _screen->setColor(12, 120, 248, 120);
_eventsManager->setCursorColor(128, 1);
_eventsManager->_intPtr._hasPalette = true;
- _graphicsManager->_fontPtr->_curFont = _bVoy->boltEntry(0x909)._fontResource;
- _graphicsManager->_fontPtr->_fontSaveBack = false;
- _graphicsManager->_fontPtr->_fontFlags = DISPFLAG_NONE;
+ _screen->_fontPtr->_curFont = _bVoy->boltEntry(0x909)._fontResource;
+ _screen->_fontPtr->_fontSaveBack = false;
+ _screen->_fontPtr->_fontFlags = DISPFLAG_NONE;
_eventsManager->getMouseInfo();
if (newX == -1) {
@@ -481,37 +481,37 @@ void VoyeurEngine::reviewTape() {
needRedraw = false;
flipPageAndWait();
- _graphicsManager->_drawPtr->_penColor = 0;
- _graphicsManager->_drawPtr->_pos = Common::Point(tempRect.left, tempRect.top);
- _graphicsManager->_backgroundPage->sFillBox(tempRect.width(), tempRect.height());
+ _screen->_drawPtr->_penColor = 0;
+ _screen->_drawPtr->_pos = Common::Point(tempRect.left, tempRect.top);
+ _screen->_backgroundPage->sFillBox(tempRect.width(), tempRect.height());
int yp = 45;
int eventNum = eventStart;
for (int lineNum = 0; lineNum < 8 && eventNum < _voy->_eventCount; ++lineNum, ++eventNum) {
- _graphicsManager->_fontPtr->_picFlags = DISPFLAG_NONE;
- _graphicsManager->_fontPtr->_picSelect = 0xff;
- _graphicsManager->_fontPtr->_picPick = 7;
- _graphicsManager->_fontPtr->_picOnOff = (lineNum == eventLine) ? 8 : 0;
- _graphicsManager->_fontPtr->_pos = Common::Point(68, yp);
- _graphicsManager->_fontPtr->_justify = ALIGN_LEFT;
- _graphicsManager->_fontPtr->_justifyWidth = 0;
- _graphicsManager->_fontPtr->_justifyHeight = 0;
+ _screen->_fontPtr->_picFlags = DISPFLAG_NONE;
+ _screen->_fontPtr->_picSelect = 0xff;
+ _screen->_fontPtr->_picPick = 7;
+ _screen->_fontPtr->_picOnOff = (lineNum == eventLine) ? 8 : 0;
+ _screen->_fontPtr->_pos = Common::Point(68, yp);
+ _screen->_fontPtr->_justify = ALIGN_LEFT;
+ _screen->_fontPtr->_justifyWidth = 0;
+ _screen->_fontPtr->_justifyHeight = 0;
Common::String msg = _eventsManager->getEvidString(eventNum);
- _graphicsManager->_backgroundPage->drawText(msg);
+ _screen->_backgroundPage->drawText(msg);
yp += 15;
}
- _graphicsManager->_vPort->addSaveRect(
- _graphicsManager->_vPort->_lastPage, tempRect);
+ _screen->_vPort->addSaveRect(
+ _screen->_vPort->_lastPage, tempRect);
flipPageAndWait();
- _graphicsManager->_vPort->addSaveRect(
- _graphicsManager->_vPort->_lastPage, tempRect);
+ _screen->_vPort->addSaveRect(
+ _screen->_vPort->_lastPage, tempRect);
}
- _graphicsManager->sDrawPic(cursor, _graphicsManager->_vPort,
+ _screen->sDrawPic(cursor, _screen->_vPort,
_eventsManager->getMousePos());
flipPageAndWait();
@@ -543,34 +543,34 @@ void VoyeurEngine::reviewTape() {
flipPageAndWait();
- _graphicsManager->_drawPtr->_penColor = 0;
- _graphicsManager->_drawPtr->_pos = Common::Point(tempRect.left, tempRect.top);
- _graphicsManager->_backgroundPage->sFillBox(tempRect.width(), tempRect.height());
+ _screen->_drawPtr->_penColor = 0;
+ _screen->_drawPtr->_pos = Common::Point(tempRect.left, tempRect.top);
+ _screen->_backgroundPage->sFillBox(tempRect.width(), tempRect.height());
int yp = 45;
int eventNum = eventStart;
for (int idx = 0; idx < 8 && eventNum < _voy->_eventCount; ++idx, ++eventNum) {
- _graphicsManager->_fontPtr->_picFlags = DISPFLAG_NONE;
- _graphicsManager->_fontPtr->_picSelect = 0xff;
- _graphicsManager->_fontPtr->_picPick = 7;
- _graphicsManager->_fontPtr->_picOnOff = (idx == eventLine) ? 8 : 0;
- _graphicsManager->_fontPtr->_pos = Common::Point(68, yp);
- _graphicsManager->_fontPtr->_justify = ALIGN_LEFT;
- _graphicsManager->_fontPtr->_justifyWidth = 0;
- _graphicsManager->_fontPtr->_justifyHeight = 0;
+ _screen->_fontPtr->_picFlags = DISPFLAG_NONE;
+ _screen->_fontPtr->_picSelect = 0xff;
+ _screen->_fontPtr->_picPick = 7;
+ _screen->_fontPtr->_picOnOff = (idx == eventLine) ? 8 : 0;
+ _screen->_fontPtr->_pos = Common::Point(68, yp);
+ _screen->_fontPtr->_justify = ALIGN_LEFT;
+ _screen->_fontPtr->_justifyWidth = 0;
+ _screen->_fontPtr->_justifyHeight = 0;
Common::String msg = _eventsManager->getEvidString(eventNum);
- _graphicsManager->_backgroundPage->drawText(msg);
+ _screen->_backgroundPage->drawText(msg);
yp += 15;
}
- _graphicsManager->_vPort->addSaveRect(
- _graphicsManager->_vPort->_lastPage, tempRect);
+ _screen->_vPort->addSaveRect(
+ _screen->_vPort->_lastPage, tempRect);
flipPageAndWait();
- _graphicsManager->_vPort->addSaveRect(
- _graphicsManager->_vPort->_lastPage, tempRect);
+ _screen->_vPort->addSaveRect(
+ _screen->_vPort->_lastPage, tempRect);
flipPageAndWait();
_eventsManager->getMouseInfo();
@@ -650,7 +650,7 @@ void VoyeurEngine::reviewTape() {
newY = _eventsManager->getMousePos().y;
_voy->_fadingType = 0;
_voy->_viewBounds = nullptr;
- _graphicsManager->_vPort->setupViewPort(NULL);
+ _screen->_vPort->setupViewPort(NULL);
if (_currentVocId != -1) {
_voy->_vocSecondsOffset = _voy->_RTVNum - _voy->_musicStartTime;
@@ -673,13 +673,13 @@ void VoyeurEngine::reviewTape() {
_voy->_vocSecondsOffset = e._computerOn;
_bVoy->getBoltGroup(0x7F00);
- _graphicsManager->_backgroundPage = _bVoy->boltEntry(0x7F00 +
+ _screen->_backgroundPage = _bVoy->boltEntry(0x7F00 +
BLIND_TABLE[_audioVideoId])._picResource;
- _graphicsManager->_backColors = _bVoy->boltEntry(0x7F01 +
+ _screen->_backColors = _bVoy->boltEntry(0x7F01 +
BLIND_TABLE[_audioVideoId])._cMapResource;
- _graphicsManager->_vPort->setupViewPort(_graphicsManager->_backgroundPage);
- _graphicsManager->_backColors->startFade();
+ _screen->_vPort->setupViewPort(_screen->_backgroundPage);
+ _screen->_backColors->startFade();
flipPageAndWaitForFade();
_eventsManager->_intPtr._flashStep = 1;
@@ -725,16 +725,16 @@ void VoyeurEngine::reviewTape() {
}
}
- _graphicsManager->_fontPtr->_curFont = _bVoy->boltEntry(0x101)._fontResource;
+ _screen->_fontPtr->_curFont = _bVoy->boltEntry(0x101)._fontResource;
- _graphicsManager->_vPort->fillPic(0);
+ _screen->_vPort->fillPic(0);
flipPageAndWait();
_bVoy->freeBoltGroup(0x900);
}
void VoyeurEngine::doGossip() {
- _graphicsManager->resetPalette();
- _graphicsManager->screenReset();
+ _screen->resetPalette();
+ _screen->screenReset();
if (!_bVoy->getBoltGroup(0x300))
return;
@@ -752,7 +752,7 @@ void VoyeurEngine::doGossip() {
// Transfer initial background to video decoder
PictureResource videoFrame(decoder.getRL2VideoTrack()->getBackSurface());
bgPic->_bounds.moveTo(0, 0);
- _graphicsManager->sDrawPic(bgPic, &videoFrame, Common::Point(0, 0));
+ _screen->sDrawPic(bgPic, &videoFrame, Common::Point(0, 0));
byte *frameNumsP = _bVoy->memberAddr(0x309);
byte *posP = _bVoy->boltEntry(0x30A)._data;
@@ -762,8 +762,8 @@ void VoyeurEngine::doGossip() {
decoder.close();
// Reset the palette and clear the screen
- _graphicsManager->resetPalette();
- _graphicsManager->screenReset();
+ _screen->resetPalette();
+ _screen->screenReset();
// Play interview video
RL2Decoder decoder2;
@@ -775,7 +775,7 @@ void VoyeurEngine::doGossip() {
decoder2.close();
_bVoy->freeBoltGroup(0x300);
- _graphicsManager->screenReset();
+ _screen->screenReset();
}
void VoyeurEngine::doTapePlaying() {
@@ -783,14 +783,14 @@ void VoyeurEngine::doTapePlaying() {
return;
_eventsManager->getMouseInfo();
- _graphicsManager->_backColors = _bVoy->boltEntry(0xA01)._cMapResource;
- _graphicsManager->_backgroundPage = _bVoy->boltEntry(0xA00)._picResource;
+ _screen->_backColors = _bVoy->boltEntry(0xA01)._cMapResource;
+ _screen->_backgroundPage = _bVoy->boltEntry(0xA00)._picResource;
PictureResource *pic = _bVoy->boltEntry(0xA02)._picResource;
VInitCycleResource *cycle = _bVoy->boltEntry(0xA05)._vInitCycleResource;
- _graphicsManager->_vPort->setupViewPort(_graphicsManager->_backgroundPage);
- _graphicsManager->sDrawPic(pic, _graphicsManager->_vPort, Common::Point(57, 30));
- _graphicsManager->_backColors->startFade();
+ _screen->_vPort->setupViewPort(_screen->_backgroundPage);
+ _screen->sDrawPic(pic, _screen->_vPort, Common::Point(57, 30));
+ _screen->_backColors->startFade();
flipPageAndWaitForFade();
cycle->vStartCycle();
@@ -932,9 +932,9 @@ int VoyeurEngine::getChooseButton() {
+ 6)._rectResource->_entries;
int selectedIndex = -1;
- _graphicsManager->_vPort->setupViewPort(_graphicsManager->_backgroundPage);
- _graphicsManager->_backColors->_steps = 0;
- _graphicsManager->_backColors->startFade();
+ _screen->_vPort->setupViewPort(_screen->_backgroundPage);
+ _screen->_backColors->_steps = 0;
+ _screen->_backColors->startFade();
flipPageAndWait();
_voy->_viewBounds = _bVoy->boltEntry(_playStampGroupId + 7)._rectResource;
@@ -955,7 +955,7 @@ int VoyeurEngine::getChooseButton() {
selectedIndex = idx;
if (selectedIndex != prevIndex) {
PictureResource *btnPic = _bVoy->boltEntry(_playStampGroupId + 8 + idx)._picResource;
- _graphicsManager->sDrawPic(btnPic, _graphicsManager->_vPort,
+ _screen->sDrawPic(btnPic, _screen->_vPort,
Common::Point(106, 200));
cursorPic = _bVoy->boltEntry(_playStampGroupId + 4)._picResource;
@@ -967,11 +967,11 @@ int VoyeurEngine::getChooseButton() {
if (selectedIndex == -1) {
cursorPic = _bVoy->boltEntry(_playStampGroupId + 2)._picResource;
PictureResource *btnPic = _bVoy->boltEntry(_playStampGroupId + 12)._picResource;
- _graphicsManager->sDrawPic(btnPic, _graphicsManager->_vPort,
+ _screen->sDrawPic(btnPic, _screen->_vPort,
Common::Point(106, 200));
}
- _graphicsManager->sDrawPic(cursorPic, _graphicsManager->_vPort,
+ _screen->sDrawPic(cursorPic, _screen->_vPort,
Common::Point(pt.x + 13, pt.y - 12));
flipPageAndWait();
@@ -982,9 +982,9 @@ int VoyeurEngine::getChooseButton() {
}
void VoyeurEngine::makeViewFinder() {
- _graphicsManager->_backgroundPage = _bVoy->boltEntry(0x103)._picResource;
- _graphicsManager->sDrawPic(_graphicsManager->_backgroundPage,
- _graphicsManager->_vPort, Common::Point(0, 0));
+ _screen->_backgroundPage = _bVoy->boltEntry(0x103)._picResource;
+ _screen->sDrawPic(_screen->_backgroundPage,
+ _screen->_vPort, Common::Point(0, 0));
CMapResource *pal = _bVoy->boltEntry(0x104)._cMapResource;
int palOffset = 0;
@@ -1016,22 +1016,22 @@ void VoyeurEngine::makeViewFinder() {
break;
}
- _graphicsManager->_vPort->drawIfaceTime();
+ _screen->_vPort->drawIfaceTime();
doTimeBar();
pal->startFade();
flipPageAndWaitForFade();
- _graphicsManager->setColor(241, 105, 105, 105);
- _graphicsManager->setColor(242, 105, 105, 105);
- _graphicsManager->setColor(243, 105, 105, 105);
- _graphicsManager->setColor(palOffset + 241, 219, 235, 235);
+ _screen->setColor(241, 105, 105, 105);
+ _screen->setColor(242, 105, 105, 105);
+ _screen->setColor(243, 105, 105, 105);
+ _screen->setColor(palOffset + 241, 219, 235, 235);
_eventsManager->_intPtr._hasPalette = true;
}
void VoyeurEngine::makeViewFinderP() {
- _graphicsManager->screenReset();
+ _screen->screenReset();
}
void VoyeurEngine::initIFace() {
@@ -1077,7 +1077,7 @@ void VoyeurEngine::initIFace() {
void VoyeurEngine::doScroll(const Common::Point &pt) {
Common::Rect clipRect(72, 47, 72 + 240, 47 + 148);
- _graphicsManager->_vPort->setupViewPort(NULL, &clipRect);
+ _screen->_vPort->setupViewPort(NULL, &clipRect);
int base = 0;
switch (_voy->_transitionId) {
@@ -1101,18 +1101,18 @@ void VoyeurEngine::doScroll(const Common::Point &pt) {
if (base) {
PictureResource *pic = _bVoy->boltEntry(base + 3)._picResource;
- _graphicsManager->sDrawPic(pic, _graphicsManager->_vPort, Common::Point(784 - pt.x - 712, 150 - pt.y - 104));
+ _screen->sDrawPic(pic, _screen->_vPort, Common::Point(784 - pt.x - 712, 150 - pt.y - 104));
pic = _bVoy->boltEntry(base + 4)._picResource;
- _graphicsManager->sDrawPic(pic, _graphicsManager->_vPort, Common::Point(784 - pt.x - 712, 150 - pt.y - 44));
+ _screen->sDrawPic(pic, _screen->_vPort, Common::Point(784 - pt.x - 712, 150 - pt.y - 44));
pic = _bVoy->boltEntry(base + 5)._picResource;
- _graphicsManager->sDrawPic(pic, _graphicsManager->_vPort, Common::Point(784 - pt.x - 712, 150 - pt.y + 16));
+ _screen->sDrawPic(pic, _screen->_vPort, Common::Point(784 - pt.x - 712, 150 - pt.y + 16));
pic = _bVoy->boltEntry(base + 6)._picResource;
- _graphicsManager->sDrawPic(pic, _graphicsManager->_vPort, Common::Point(784 - pt.x - 712, 150 - pt.y + 76));
+ _screen->sDrawPic(pic, _screen->_vPort, Common::Point(784 - pt.x - 712, 150 - pt.y + 76));
pic = _bVoy->boltEntry(base + 7)._picResource;
- _graphicsManager->sDrawPic(pic, _graphicsManager->_vPort, Common::Point(784 - pt.x - 712, 150 - pt.y + 136));
+ _screen->sDrawPic(pic, _screen->_vPort, Common::Point(784 - pt.x - 712, 150 - pt.y + 136));
}
- _graphicsManager->_vPort->setupViewPort(NULL);
+ _screen->_vPort->setupViewPort(NULL);
}
void VoyeurEngine::checkTransition() {
@@ -1124,7 +1124,7 @@ void VoyeurEngine::checkTransition() {
// Only proceed if a valid day string was returned
if (!day.empty()) {
- _graphicsManager->fadeDownICF(6);
+ _screen->fadeDownICF(6);
// Get the time of day string
time = getTimeOfDay();
@@ -1163,7 +1163,7 @@ Common::String VoyeurEngine::getTimeOfDay() {
}
int VoyeurEngine::doComputerText(int maxLen) {
- FontInfoResource &font = *_graphicsManager->_fontPtr;
+ FontInfoResource &font = *_screen->_fontPtr;
int totalChars = 0;
font._curFont = _bVoy->boltEntry(0x4910)._fontResource;
@@ -1180,7 +1180,7 @@ int VoyeurEngine::doComputerText(int maxLen) {
font._justifyWidth = 384;
font._justifyHeight = 100;
font._pos = Common::Point(128, 100);
- _graphicsManager->_vPort->drawText(END_OF_MESSAGE);
+ _screen->_vPort->drawText(END_OF_MESSAGE);
} else if (_voy->_RTVNum < _voy->_computerTimeMin && maxLen == 9999) {
if (_currentVocId != -1)
_soundManager->startVOCPlay(_currentVocId);
@@ -1188,7 +1188,7 @@ int VoyeurEngine::doComputerText(int maxLen) {
font._justifyWidth = 384;
font._justifyHeight = 100;
font._pos = Common::Point(120, 100);
- _graphicsManager->_vPort->drawText(START_OF_MESSAGE);
+ _screen->_vPort->drawText(START_OF_MESSAGE);
} else {
char *msg = (char *)_bVoy->memberAddr(0x4900 + _voy->_computerTextId);
font._pos = Common::Point(96, 60);
@@ -1206,14 +1206,14 @@ int VoyeurEngine::doComputerText(int maxLen) {
if (c == '\0') {
if (showEnd) {
_eventsManager->delay(90);
- _graphicsManager->_drawPtr->_pos = Common::Point(96, 54);
- _graphicsManager->_drawPtr->_penColor = 254;
- _graphicsManager->_vPort->sFillBox(196, 124);
- _graphicsManager->_fontPtr->_justify = ALIGN_LEFT;
- _graphicsManager->_fontPtr->_justifyWidth = 384;
- _graphicsManager->_fontPtr->_justifyHeight = 100;
- _graphicsManager->_fontPtr->_pos = Common::Point(128, 100);
- _graphicsManager->_vPort->drawText(END_OF_MESSAGE);
+ _screen->_drawPtr->_pos = Common::Point(96, 54);
+ _screen->_drawPtr->_penColor = 254;
+ _screen->_vPort->sFillBox(196, 124);
+ _screen->_fontPtr->_justify = ALIGN_LEFT;
+ _screen->_fontPtr->_justifyWidth = 384;
+ _screen->_fontPtr->_justifyHeight = 100;
+ _screen->_fontPtr->_pos = Common::Point(128, 100);
+ _screen->_vPort->drawText(END_OF_MESSAGE);
}
break;
}
@@ -1223,20 +1223,20 @@ int VoyeurEngine::doComputerText(int maxLen) {
yp += 10;
} else {
_eventsManager->delay(90);
- _graphicsManager->_drawPtr->_pos = Common::Point(96, 54);
- _graphicsManager->_drawPtr->_penColor = 255;
- _graphicsManager->_vPort->sFillBox(196, 124);
+ _screen->_drawPtr->_pos = Common::Point(96, 54);
+ _screen->_drawPtr->_penColor = 255;
+ _screen->_vPort->sFillBox(196, 124);
yp = 60;
}
- _graphicsManager->_fontPtr->_pos = Common::Point(96, yp);
+ _screen->_fontPtr->_pos = Common::Point(96, yp);
} else if (c == '_') {
showEnd = false;
} else {
- _graphicsManager->_fontPtr->_justify = ALIGN_LEFT;
- _graphicsManager->_fontPtr->_justifyWidth = 0;
- _graphicsManager->_fontPtr->_justifyHeight = 0;
- _graphicsManager->_vPort->drawText(Common::String(c));
+ _screen->_fontPtr->_justify = ALIGN_LEFT;
+ _screen->_fontPtr->_justifyWidth = 0;
+ _screen->_fontPtr->_justifyHeight = 0;
+ _screen->_vPort->drawText(Common::String(c));
_eventsManager->delay(4);
}
@@ -1251,7 +1251,7 @@ int VoyeurEngine::doComputerText(int maxLen) {
flipPageAndWait();
- _graphicsManager->_fontPtr->_curFont = _bVoy->boltEntry(0x101)._fontResource;
+ _screen->_fontPtr->_curFont = _bVoy->boltEntry(0x101)._fontResource;
return totalChars;
}
@@ -1263,7 +1263,7 @@ void VoyeurEngine::getComputerBrush() {
int xp = (384 - pic->_bounds.width()) / 2;
int yp = (240 - pic->_bounds.height()) / 2 - 4;
- _graphicsManager->_vPort->drawPicPerm(pic, Common::Point(xp, yp));
+ _screen->_vPort->drawPicPerm(pic, Common::Point(xp, yp));
CMapResource *pal = _bVoy->boltEntry(0x490F)._cMapResource;
pal->startFade();
@@ -1280,17 +1280,17 @@ void VoyeurEngine::doTimeBar() {
int height = ((_voy->_RTVLimit - _voy->_RTVNum) * 59) / _voy->_RTVLimit;
int fullHeight = MAX(151 - height, 93);
- _graphicsManager->_drawPtr->_penColor = 134;
- _graphicsManager->_drawPtr->_pos = Common::Point(39, 92);
+ _screen->_drawPtr->_penColor = 134;
+ _screen->_drawPtr->_pos = Common::Point(39, 92);
- _graphicsManager->_vPort->sFillBox(6, fullHeight - 92);
+ _screen->_vPort->sFillBox(6, fullHeight - 92);
if (height > 0) {
- _graphicsManager->setColor(215, 238, 238, 238);
+ _screen->setColor(215, 238, 238, 238);
_eventsManager->_intPtr._hasPalette = true;
- _graphicsManager->_drawPtr->_penColor = 215;
- _graphicsManager->_drawPtr->_pos = Common::Point(39, fullHeight);
- _graphicsManager->_vPort->sFillBox(6, height);
+ _screen->_drawPtr->_penColor = 215;
+ _screen->_drawPtr->_pos = Common::Point(39, fullHeight);
+ _screen->_vPort->sFillBox(6, height);
}
}
}
@@ -1303,9 +1303,9 @@ void VoyeurEngine::flashTimeBar() {
_flashTimeVal = _eventsManager->_intPtr._flashTimer;
if (_flashTimeFlag)
- _graphicsManager->setColor(240, 220, 20, 20);
+ _screen->setColor(240, 220, 20, 20);
else
- _graphicsManager->setColor(240, 220, 220, 220);
+ _screen->setColor(240, 220, 220, 220);
_eventsManager->_intPtr._hasPalette = true;
_flashTimeFlag = !_flashTimeFlag;
@@ -1343,7 +1343,7 @@ void VoyeurEngine::doEvidDisplay(int evidId, int eventId) {
_bVoy->getBoltGroup(_voy->_boltGroupId2);
PictureResource *pic = _bVoy->boltEntry(_voy->_boltGroupId2 + evidId * 2)._picResource;
- _graphicsManager->sDrawPic(pic, _graphicsManager->_vPort, Common::Point(
+ _screen->sDrawPic(pic, _screen->_vPort, Common::Point(
(384 - pic->_bounds.width()) / 2, (240 - pic->_bounds.height()) / 2));
_bVoy->freeBoltMember(_voy->_boltGroupId2 + evidId * 2);
@@ -1394,7 +1394,7 @@ void VoyeurEngine::doEvidDisplay(int evidId, int eventId) {
continue;
pic = _voy->_evPicPtrs[arrIndex];
- _graphicsManager->sDrawPic(pic, _graphicsManager->_vPort,
+ _screen->sDrawPic(pic, _screen->_vPort,
Common::Point((384 - pic->_bounds.width()) / 2,
(240 - pic->_bounds.height()) / 2));
_voy->_evCmPtrs[arrIndex]->startFade();
diff --git a/engines/wage/combat.cpp b/engines/wage/combat.cpp
new file mode 100644
index 0000000000..4f2956d7d2
--- /dev/null
+++ b/engines/wage/combat.cpp
@@ -0,0 +1,916 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * MIT License:
+ *
+ * Copyright (c) 2009 Alexei Svitkine, Eugene Sandulenko
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include "wage/wage.h"
+#include "wage/entities.h"
+#include "wage/randomhat.h"
+#include "wage/world.h"
+
+namespace Wage {
+
+Obj *WageEngine::getOffer() {
+ if (_offer != NULL) {
+ Chr *owner = _offer->_currentOwner;
+ if (owner == NULL || owner->_playerCharacter || owner->_currentScene != _world->_player->_currentScene) {
+ _offer = NULL;
+ }
+ }
+ return _offer;
+}
+
+Chr *WageEngine::getMonster() {
+ if (_monster != NULL && _monster->_currentScene != _world->_player->_currentScene) {
+ _monster = NULL;
+ }
+ return _monster;
+}
+
+void WageEngine::encounter(Chr *player, Chr *chr) {
+ char buf[512];
+
+ snprintf(buf, 512, "You encounter %s%s.", chr->_nameProperNoun ? "" : getIndefiniteArticle(chr->_name),
+ chr->_name.c_str());
+ appendText(buf);
+
+ if (!chr->_initialComment.empty())
+ appendText(chr->_initialComment.c_str());
+
+ if (chr->_armor[Chr::HEAD_ARMOR] != NULL) {
+ snprintf(buf, 512, "%s%s is wearing %s.", chr->getDefiniteArticle(true), chr->_name.c_str(),
+ getIndefiniteArticle(chr->_armor[Chr::HEAD_ARMOR]->_name));
+ appendText(buf);
+ }
+ if (chr->_armor[Chr::BODY_ARMOR] != NULL) {
+ snprintf(buf, 512, "%s is protected by %s%s.", getGenderSpecificPronoun(chr->_gender, true),
+ prependGenderSpecificPronoun(chr->_gender), chr->_armor[Chr::BODY_ARMOR]->_name.c_str());
+ appendText(buf);
+ }
+ if (chr->_armor[Chr::SHIELD_ARMOR] != NULL) {
+ Obj *obj = chr->_armor[Chr::SHIELD_ARMOR];
+
+ snprintf(buf, 512, "%s carries %s%s.", getGenderSpecificPronoun(chr->_gender, true),
+ obj->_namePlural ? "" : getIndefiniteArticle(obj->_name), obj->_name.c_str());
+ appendText(buf);
+ }
+}
+
+void WageEngine::performCombatAction(Chr *npc, Chr *player) {
+ if (npc->_context._frozen)
+ return;
+
+ RandomHat hat(_rnd);
+
+ bool winning = (npc->_context._statVariables[PHYS_HIT_CUR] > player->_context._statVariables[PHYS_HIT_CUR]);
+ int validMoves = getValidMoveDirections(npc);
+ ObjArray *weapons = npc->getWeapons(false);
+ ObjArray *magics = npc->getMagicalObjects();
+ // TODO: Figure out under what circumstances we need to add +1
+ // for the chance (e.g. only when all values were set to 0?).
+ if (winning) {
+ if (!_world->_weaponMenuDisabled) {
+ if (!weapons->empty())
+ hat.addTokens(kTokWeapons, npc->_winningWeapons + 1);
+ if (!magics->empty())
+ hat.addTokens(kTokMagic, npc->_winningMagic);
+ }
+ if (validMoves != 0)
+ hat.addTokens(kTokRun, npc->_winningRun + 1);
+ if (!npc->_inventory.empty())
+ hat.addTokens(kTokOffer, npc->_winningOffer + 1);
+ } else {
+ if (!_world->_weaponMenuDisabled) {
+ if (!weapons->empty())
+ hat.addTokens(kTokWeapons, npc->_losingWeapons + 1);
+ if (!magics->empty())
+ hat.addTokens(kTokMagic, npc->_losingMagic);
+ }
+ if (validMoves != 0)
+ hat.addTokens(kTokRun, npc->_losingRun + 1);
+ if (!npc->_inventory.empty())
+ hat.addTokens(kTokOffer, npc->_losingOffer + 1);
+ }
+
+ ObjList *objs = &npc->_currentScene->_objs;
+ if (npc->_inventory.size() < npc->_maximumCarriedObjects) {
+ int cnt = 0;
+ for (ObjList::const_iterator it = objs->begin(); it != objs->end(); ++it, ++cnt) {
+ if ((*it)->_type != Obj::IMMOBILE_OBJECT) {
+ // TODO: I'm not sure what the chance should be here.
+ hat.addTokens(cnt, 123);
+ }
+ }
+ }
+
+ int token = hat.drawToken();
+ switch (token) {
+ case kTokWeapons:
+ // TODO: I think the monster should choose the "best" weapon.
+ performAttack(npc, player, weapons->operator[](_rnd->getRandomNumber(weapons->size() - 1)));
+ break;
+ case kTokMagic:
+ // TODO: I think the monster should choose the "best" magic.
+ performMagic(npc, player, magics->operator[](_rnd->getRandomNumber(magics->size() - 1)));
+ break;
+ case kTokRun:
+ performMove(npc, validMoves);
+ break;
+ case kTokOffer:
+ performOffer(npc, player);
+ break;
+ case kTokNone:
+ break;
+ default:
+ {
+ int cnt = 0;
+ for (ObjList::const_iterator it = objs->begin(); it != objs->end(); ++it, ++cnt)
+ if (cnt == token)
+ performTake(npc, *it);
+ break;
+ }
+ }
+
+ delete weapons;
+ delete magics;
+}
+
+static const char *const targets[] = { "head", "chest", "side" };
+
+void WageEngine::performAttack(Chr *attacker, Chr *victim, Obj *weapon) {
+ if (_world->_weaponMenuDisabled)
+ return;
+
+ // TODO: verify that a player not aiming will always target the chest??
+ int targetIndex = -1;
+ char buf[256];
+
+ if (weapon->_type != Obj::MAGICAL_OBJECT) {
+ if (attacker->_playerCharacter) {
+ targetIndex = _aim;
+ } else {
+ targetIndex = _rnd->getRandomNumber(ARRAYSIZE(targets) - 1);
+ _opponentAim = targetIndex + 1;
+ }
+
+ if (!attacker->_playerCharacter) {
+ snprintf(buf, 256, "%s%s %ss %s%s at %s%s's %s.",
+ attacker->getDefiniteArticle(true), attacker->_name.c_str(),
+ weapon->_operativeVerb.c_str(),
+ prependGenderSpecificPronoun(attacker->_gender), weapon->_name.c_str(),
+ victim->getDefiniteArticle(true), victim->_name.c_str(),
+ targets[targetIndex]);
+ appendText(buf);
+ }
+ } else if (!attacker->_playerCharacter) {
+ snprintf(buf, 256, "%s%s %ss %s%s at %s%s.",
+ attacker->getDefiniteArticle(true), attacker->_name.c_str(),
+ weapon->_operativeVerb.c_str(),
+ prependGenderSpecificPronoun(attacker->_gender), weapon->_name.c_str(),
+ victim->getDefiniteArticle(true), victim->_name.c_str());
+ appendText(buf);
+ }
+
+ playSound(weapon->_sound);
+
+ bool usesDecremented = false;
+ int chance = _rnd->getRandomNumber(255);
+ // TODO: what about obj accuracy
+ if (chance < attacker->_physicalAccuracy) {
+ usesDecremented = attackHit(attacker, victim, weapon, targetIndex);
+ } else if (weapon->_type != Obj::MAGICAL_OBJECT) {
+ appendText("A miss!");
+ } else if (attacker->_playerCharacter) {
+ appendText("The spell has no effect.");
+ }
+
+ if (!usesDecremented) {
+ decrementUses(weapon);
+ }
+}
+
+void WageEngine::decrementUses(Obj *obj) {
+ int numberOfUses = obj->_numberOfUses;
+ if (numberOfUses != -1) {
+ numberOfUses--;
+ if (numberOfUses > 0) {
+ obj->_numberOfUses = numberOfUses;
+ } else {
+ if (!obj->_failureMessage.empty()) {
+ appendText(obj->_failureMessage.c_str());
+ }
+ if (obj->_returnToRandomScene) {
+ _world->move(obj, _world->getRandomScene());
+ } else {
+ _world->move(obj, _world->_storageScene);
+ }
+ obj->resetState(obj->_currentOwner, obj->_currentScene);
+ }
+ }
+}
+
+bool WageEngine::attackHit(Chr *attacker, Chr *victim, Obj *weapon, int targetIndex) {
+ bool receivedHitTextPrinted = false;
+ char buf[512];
+
+ if (targetIndex != -1) {
+ Obj *armor = victim->_armor[targetIndex];
+ if (armor != NULL) {
+ // TODO: Absorb some damage.
+ snprintf(buf, 512, "%s%s's %s weakens the impact of %s%s's %s.",
+ victim->getDefiniteArticle(true), victim->_name.c_str(),
+ victim->_armor[targetIndex]->_name.c_str(),
+ attacker->getDefiniteArticle(false), attacker->_name.c_str(),
+ weapon->_name.c_str());
+ appendText(buf);
+ decrementUses(armor);
+ } else {
+ snprintf(buf, 512, "A hit to the %s!", targets[targetIndex]);
+ appendText(buf);
+ }
+ playSound(attacker->_scoresHitSound);
+ appendText(attacker->_scoresHitComment.c_str());
+ playSound(victim->_receivesHitSound);
+ appendText(victim->_receivesHitComment.c_str());
+ receivedHitTextPrinted = true;
+ } else if (weapon->_type == Obj::MAGICAL_OBJECT) {
+ appendText(weapon->_useMessage.c_str());
+ appendText("The spell is effective!");
+ }
+
+ bool causesPhysicalDamage = true;
+ bool causesSpiritualDamage = false;
+ bool freezesOpponent = false;
+ bool usesDecremented = false;
+
+ if (weapon->_type == Obj::THROW_WEAPON) {
+ _world->move(weapon, victim->_currentScene);
+ } else if (weapon->_type == Obj::MAGICAL_OBJECT) {
+ int type = weapon->_attackType;
+ causesPhysicalDamage = (type == Obj::CAUSES_PHYSICAL_DAMAGE || type == Obj::CAUSES_PHYSICAL_AND_SPIRITUAL_DAMAGE);
+ causesSpiritualDamage = (type == Obj::CAUSES_SPIRITUAL_DAMAGE || type == Obj::CAUSES_PHYSICAL_AND_SPIRITUAL_DAMAGE);
+ freezesOpponent = (type == Obj::FREEZES_OPPONENT);
+ }
+
+ if (causesPhysicalDamage) {
+ victim->_context._userVariables[PHYS_HIT_CUR] -= weapon->_damage;
+
+ /* Do it here to get the right order of messages in case of death. */
+ decrementUses(weapon);
+ usesDecremented = true;
+
+ if (victim->_context._userVariables[PHYS_HIT_CUR] < 0) {
+ playSound(victim->_dyingSound);
+ appendText(victim->_dyingWords.c_str());
+ snprintf(buf, 512, "%s%s is dead!", victim->getDefiniteArticle(true), victim->_name.c_str());
+ appendText(buf);
+
+ attacker->_context._kills++;
+ attacker->_context._experience += victim->_context._userVariables[SPIR_HIT_CUR] + victim->_context._userVariables[PHYS_HIT_CUR];
+
+ if (!victim->_playerCharacter && !victim->_inventory.empty()) {
+ Scene *currentScene = victim->_currentScene;
+
+ for (int i = victim->_inventory.size() - 1; i >= 0; i--) {
+ _world->move(victim->_inventory[i], currentScene);
+ }
+ Common::String *s = getGroundItemsList(currentScene);
+ appendText(s->c_str());
+ delete s;
+ }
+ _world->move(victim, _world->_storageScene);
+ } else if (attacker->_playerCharacter && !receivedHitTextPrinted) {
+ double physicalPercent = (double)victim->_context._userVariables[SPIR_HIT_CUR] /
+ victim->_context._userVariables[SPIR_HIT_BAS];
+ snprintf(buf, 512, "%s%s's condition appears to be %s.",
+ victim->getDefiniteArticle(true), victim->_name.c_str(),
+ getPercentMessage(physicalPercent));
+ appendText(buf);
+ }
+ }
+
+ if (causesSpiritualDamage) {
+ /* TODO */
+ warning("TODO: Spiritual damage");
+ }
+
+ if (freezesOpponent) {
+ victim->_context._frozen = true;
+ }
+
+ return usesDecremented;
+}
+
+void WageEngine::performMagic(Chr *attacker, Chr *victim, Obj *magicalObject) {
+ switch (magicalObject->_attackType) {
+ case Obj::HEALS_PHYSICAL_DAMAGE:
+ case Obj::HEALS_SPIRITUAL_DAMAGE:
+ case Obj::HEALS_PHYSICAL_AND_SPIRITUAL_DAMAGE:
+ performHealingMagic(attacker, magicalObject);
+ return;
+ }
+
+ performAttack(attacker, victim, magicalObject);
+}
+
+void WageEngine::performHealingMagic(Chr *chr, Obj *magicalObject) {
+ char buf[512];
+
+ if (!chr->_playerCharacter) {
+ snprintf(buf, 512, "%s%s %ss %s%s.",
+ chr->getDefiniteArticle(true), chr->_name.c_str(),
+ magicalObject->_operativeVerb.c_str(),
+ getIndefiniteArticle(magicalObject->_name), magicalObject->_name.c_str());
+ appendText(buf);
+ }
+
+ uint chance = _rnd->getRandomNumber(255);
+ if (chance < magicalObject->_accuracy) {
+ int type = magicalObject->_attackType;
+
+ if (type == Obj::HEALS_PHYSICAL_DAMAGE || type == Obj::HEALS_PHYSICAL_AND_SPIRITUAL_DAMAGE)
+ chr->_context._statVariables[PHYS_HIT_CUR] += magicalObject->_damage;
+
+ if (type == Obj::HEALS_SPIRITUAL_DAMAGE || type == Obj::HEALS_PHYSICAL_AND_SPIRITUAL_DAMAGE)
+ chr->_context._statVariables[SPIR_HIT_CUR] += magicalObject->_damage;
+
+ playSound(magicalObject->_sound);
+ appendText(magicalObject->_useMessage.c_str());
+
+ // TODO: what if enemy heals himself?
+ if (chr->_playerCharacter) {
+ double physicalPercent = (double)chr->_context._statVariables[PHYS_HIT_CUR] / chr->_context._statVariables[PHYS_HIT_BAS];
+ double spiritualPercent = (double)chr->_context._statVariables[SPIR_HIT_CUR] / chr->_context._statVariables[SPIR_HIT_BAS];
+ snprintf(buf, 256, "Your physical condition is %s.", getPercentMessage(physicalPercent));
+ appendText(buf);
+
+ snprintf(buf, 256, "Your spiritual condition is %s.", getPercentMessage(spiritualPercent));
+ appendText(buf);
+ }
+ }
+
+ decrementUses(magicalObject);
+}
+
+static const int directionsX[] = { 0, 0, 1, -1 };
+static const int directionsY[] = { -1, 1, 0, 0 };
+static const char *const directionsS[] = { "north", "south", "east", "west" };
+
+void WageEngine::performMove(Chr *chr, int validMoves) {
+ // count how many valid moves we have
+ int numValidMoves = 0;
+
+ for (int i = 0; i < 4; i++)
+ if ((validMoves & (1 << i)) != 0)
+ numValidMoves++;
+
+ // Now pick random dir
+ int dirNum = _rnd->getRandomNumber(numValidMoves - 1);
+ int dir = 0;
+
+ // And get it
+ for (int i = 0; i < 4; i++)
+ if ((validMoves & (1 << i)) != 0) {
+ if (dirNum == 0) {
+ dir = i;
+ break;
+ }
+ dirNum--;
+ }
+
+ char buf[256];
+ snprintf(buf, 256, "%s%s runs %s.", chr->getDefiniteArticle(true), chr->_name.c_str(), directionsS[dir]);
+ appendText(buf);
+
+ _running = chr;
+ Scene *currentScene = chr->_currentScene;
+ int destX = currentScene->_worldX + directionsX[dir];
+ int destY = currentScene->_worldY + directionsY[dir];
+
+ _world->move(chr, _world->getSceneAt(destX, destY));
+}
+
+void WageEngine::performOffer(Chr *attacker, Chr *victim) {
+ /* TODO: choose in a smarter way? */
+ Obj *obj = attacker->_inventory[0];
+ char buf[512];
+
+ snprintf(buf, 512, "%s%s offers %s%s.", attacker->getDefiniteArticle(true), attacker->_name.c_str(),
+ obj->_namePlural ? "some " : getIndefiniteArticle(obj->_name), obj->_name.c_str());
+
+ appendText(buf);
+
+ _offer = obj;
+}
+
+void WageEngine::performTake(Chr *npc, Obj *obj) {
+ char buf[512];
+
+ snprintf(buf, 512, "%s%s picks up the %s%s.", npc->getDefiniteArticle(true), npc->_name.c_str(),
+ getIndefiniteArticle(obj->_name), obj->_name.c_str());
+
+ appendText(buf);
+
+ _world->move(obj, npc);
+}
+
+int WageEngine::getValidMoveDirections(Chr *npc) {
+ int directions = 0;
+ Scene *currentScene = npc->_currentScene;
+ for (int dir = 0; dir < 4; dir++) {
+ if (!currentScene->_blocked[dir]) {
+ int destX = currentScene->_worldX + directionsX[dir];
+ int destY = currentScene->_worldY + directionsY[dir];
+
+ Scene *scene = _world->getSceneAt(destX, destY);
+
+ if (scene != NULL && scene->_chrs.empty()) {
+ directions |= (1 << dir);
+ }
+ }
+ }
+
+ return directions;
+}
+
+void WageEngine::regen() {
+ Chr *player = _world->_player;
+ int curHp = player->_context._statVariables[PHYS_HIT_CUR];
+ int maxHp = player->_context._statVariables[PHYS_HIT_BAS];
+ int delta = maxHp - curHp;
+
+ if (delta > 0) {
+ int bonus = (int)(delta / (8 + _rnd->getRandomNumber(2)));
+ player->_context._statVariables[PHYS_HIT_CUR] += bonus;
+ }
+}
+
+void WageEngine::takeObj(Obj *obj) {
+ if (_world->_player->_inventory.size() >= _world->_player->_maximumCarriedObjects) {
+ appendText("Your pack is full, you must drop something.");
+ } else {
+ char buf[256];
+
+ _world->move(obj, _world->_player);
+ int type = _world->_player->wearObjIfPossible(obj);
+ if (type == Chr::HEAD_ARMOR) {
+ snprintf(buf, 256, "You are now wearing the %s.", obj->_name.c_str());
+ appendText(buf);
+ } else if (type == Chr::BODY_ARMOR) {
+ snprintf(buf, 256, "You are now wearing the %s.", obj->_name.c_str());
+ appendText(buf);
+ } else if (type == Chr::SHIELD_ARMOR) {
+ snprintf(buf, 256, "You are now wearing the %s.", obj->_name.c_str());
+ appendText(buf);
+ } else if (type == Chr::MAGIC_ARMOR) {
+ snprintf(buf, 256, "You are now wearing the %s.", obj->_name.c_str());
+ appendText(buf);
+ } else {
+ snprintf(buf, 256, "You now have the %s.", obj->_name.c_str());
+ appendText(buf);
+ }
+ appendText(obj->_clickMessage.c_str());
+ }
+}
+
+bool WageEngine::handleMoveCommand(Directions dir, const char *dirName) {
+ Scene *playerScene = _world->_player->_currentScene;
+ const char *msg = playerScene->_messages[dir].c_str();
+
+ if (!playerScene->_blocked[dir]) {
+ int destX = playerScene->_worldX + directionsX[dir];
+ int destY = playerScene->_worldY + directionsY[dir];
+
+ Scene *scene = _world->getSceneAt(destX, destY);
+
+ if (scene != NULL) {
+ if (strlen(msg) > 0) {
+ appendText(msg);
+ }
+ _world->move(_world->_player, scene);
+ return true;
+ }
+ }
+ if (strlen(msg) > 0) {
+ appendText(msg);
+ } else {
+ Common::String txt("You can't go ");
+ txt += dirName;
+ txt += ".";
+ appendText(txt.c_str());
+ }
+
+ return true;
+}
+
+bool WageEngine::handleLookCommand() {
+ appendText(_world->_player->_currentScene->_text.c_str());
+
+ Common::String *items = getGroundItemsList(_world->_player->_currentScene);
+ if (items != NULL) {
+ appendText(items->c_str());
+
+ delete items;
+ }
+
+ return true;
+}
+
+Common::String *WageEngine::getGroundItemsList(Scene *scene) {
+ ObjArray objs;
+
+ for (ObjList::const_iterator it = scene->_objs.begin(); it != scene->_objs.end(); ++it)
+ if ((*it)->_type != Obj::IMMOBILE_OBJECT)
+ objs.push_back(*it);
+
+ if (!objs.empty()) {
+ Common::String *res = new Common::String("On the ground you see ");
+ appendObjNames(*res, objs);
+ return res;
+ }
+ return NULL;
+}
+
+void WageEngine::appendObjNames(Common::String &str, const ObjArray &objs) {
+ for (uint i = 0; i < objs.size(); i++) {
+ Obj *obj = objs[i];
+
+ if (!obj->_namePlural)
+ str += getIndefiniteArticle(obj->_name);
+ else
+ str += "some ";
+
+ str += obj->_name;
+
+ if (i == objs.size() - 1) {
+ str += ".";
+ } else if (i == objs.size() - 2) {
+ if (objs.size() > 2)
+ str += ",";
+ str += " and ";
+ } else {
+ str += ", ";
+ }
+ }
+}
+
+bool WageEngine::handleInventoryCommand() {
+ Chr *player = _world->_player;
+ ObjArray objs;
+
+ for (ObjArray::const_iterator it = player->_inventory.begin(); it != player->_inventory.end(); ++it)
+ if (!player->isWearing(*it))
+ objs.push_back(*it);
+
+ if (objs.empty()) {
+ appendText("Your pack is empty.");
+ } else {
+ Common::String res("Your pack contains ");
+ appendObjNames(res, objs);
+ appendText(res.c_str());
+ }
+
+ return true;
+}
+
+static const char *const armorMessages[] = {
+ "Head protection:",
+ "Chest protection:",
+ "Shield protection:", // TODO: check message
+ "Magical protection:"
+};
+
+bool WageEngine::handleStatusCommand() {
+ Chr *player = _world->_player;
+ char buf[512];
+
+ snprintf(buf, 512, "Character name: %s%s", player->getDefiniteArticle(false), player->_name.c_str());
+ appendText(buf);
+ snprintf(buf, 512, "Experience: %d", player->_context._experience);
+ appendText(buf);
+
+ int wealth = 0;
+ for (ObjArray::const_iterator it = player->_inventory.begin(); it != player->_inventory.end(); ++it)
+ wealth += (*it)->_value;
+
+ snprintf(buf, 512, "Wealth: %d", wealth);
+ appendText(buf);
+
+ for (int i = 0; i < Chr::NUMBER_OF_ARMOR_TYPES; i++) {
+ if (player->_armor[i] != NULL) {
+ snprintf(buf, 512, "%s %s", armorMessages[i], player->_armor[i]->_name.c_str());
+ appendText(buf);
+ }
+ }
+
+ for (ObjArray::const_iterator it = player->_inventory.begin(); it != player->_inventory.end(); ++it) {
+ int uses = (*it)->_numberOfUses;
+
+ if (uses > 0) {
+ snprintf(buf, 512, "Your %s has %d uses left.", (*it)->_name.c_str(), uses);
+ appendText(buf);
+ }
+ }
+
+ printPlayerCondition(player);
+
+ _commandWasQuick = true;
+
+ return true;
+}
+
+bool WageEngine::handleRestCommand() {
+ if (getMonster() != NULL) {
+ appendText("This is no time to rest!");
+ _commandWasQuick = true;
+ } else {
+ regen();
+ printPlayerCondition(_world->_player);
+ }
+
+ return true;
+}
+
+bool WageEngine::handleAcceptCommand() {
+ Chr *chr = _offer->_currentOwner;
+
+ char buf[512];
+ snprintf(buf, 512, "%s%s lays the %s on the ground and departs peacefully.",
+ chr->getDefiniteArticle(true), chr->_name.c_str(), _offer->_name.c_str());
+ appendText(buf);
+
+ _world->move(_offer, chr->_currentScene);
+ _world->move(chr, _world->_storageScene);
+
+ return true;
+}
+
+bool WageEngine::handleTakeCommand(const char *target) {
+ Common::String t(target);
+ bool handled = false;
+
+ for (ObjList::const_iterator it = _world->_player->_currentScene->_objs.begin(); it != _world->_player->_currentScene->_objs.end(); ++it) {
+ Common::String n((*it)->_name);
+ n.toLowercase();
+
+ if (t.contains(n)) {
+ if ((*it)->_type == Obj::IMMOBILE_OBJECT) {
+ appendText("You can't move it.");
+ } else {
+ takeObj(*it);
+ }
+
+ handled = true;
+ break;
+ }
+ }
+
+ return handled;
+}
+
+bool WageEngine::handleDropCommand(const char *target) {
+ Common::String t(target);
+ bool handled = false;
+
+ t.toLowercase();
+
+ for (ObjArray::const_iterator it = _world->_player->_inventory.begin(); it != _world->_player->_inventory.end(); ++it) {
+ Common::String n((*it)->_name);
+ n.toLowercase();
+
+ if (t.contains(n)) {
+ char buf[256];
+
+ snprintf(buf, 256, "You no longer have the %s.", (*it)->_name.c_str());
+ appendText(buf);
+ _world->move(*it, _world->_player->_currentScene);
+
+ handled = true;
+ break;
+ }
+ }
+
+ return handled;
+}
+
+bool WageEngine::handleAimCommand(const char *t) {
+ bool wasHandled = true;
+ Common::String target(t);
+
+ target.toLowercase();
+
+ if (target.contains("head")) {
+ _aim = Chr::HEAD;
+ } else if (target.contains("chest")) {
+ _aim = Chr::CHEST;
+ } else if (target.contains("side")) {
+ _aim = Chr::SIDE;
+ } else {
+ wasHandled = false;
+ appendText("Please aim for the head, chest, or side.");
+ }
+
+ _commandWasQuick = true;
+
+ return wasHandled;
+}
+
+bool WageEngine::handleWearCommand(const char *t) {
+ Chr *player = _world->_player;
+ char buf[512];
+ Common::String target(t);
+ bool handled = false;
+
+ target.toLowercase();
+
+ for (ObjArray::const_iterator it = _world->_player->_inventory.begin(); it != _world->_player->_inventory.end(); ++it) {
+ Common::String n((*it)->_name);
+
+ if (target.contains(n)) {
+ if ((*it)->_type == Obj::HELMET) {
+ wearObj(*it, Chr::HEAD_ARMOR);
+ } else if ((*it)->_type == Obj::CHEST_ARMOR) {
+ wearObj(*it, Chr::BODY_ARMOR);
+ } else if ((*it)->_type == Obj::SHIELD) {
+ wearObj(*it, Chr::SHIELD_ARMOR);
+ } else if ((*it)->_type == Obj::SPIRITUAL_ARMOR) {
+ wearObj(*it, Chr::MAGIC_ARMOR);
+ } else {
+ appendText("You cannot wear that object.");
+ }
+
+ handled = true;
+ break;
+ }
+ }
+
+ for (ObjList::const_iterator it = player->_currentScene->_objs.begin(); it != player->_currentScene->_objs.end(); ++it) {
+ Common::String n((*it)->_name);
+ n.toLowercase();
+ if (target.contains(n)) {
+ snprintf(buf, 512, "First you must get the %s.", (*it)->_name.c_str());
+ appendText(buf);
+
+ handled = true;
+ break;
+ }
+ }
+
+ return handled;
+}
+
+void WageEngine::wearObj(Obj *o, int pos) {
+ Chr *player = _world->_player;
+ char buf[512];
+
+ if (player->_armor[pos] == o) {
+ snprintf(buf, 512, "You are already wearing the %s.", o->_name.c_str());
+ appendText(buf);
+ } else {
+ if (player->_armor[pos] != NULL) {
+ snprintf(buf, 512, "You are no longer wearing the %s.", player->_armor[pos]->_name.c_str());
+ appendText(buf);
+ }
+
+ player->_armor[pos] = o;
+ snprintf(buf, 512, "You are now wearing the %s.", o->_name.c_str());
+ appendText(buf);
+ }
+}
+
+
+bool WageEngine::handleOfferCommand(const char *target) {
+ Chr *player = _world->_player;
+ Chr *enemy = getMonster();
+
+ if (enemy != NULL) {
+ Common::String t(target);
+ t.toLowercase();
+
+ for (ObjArray::const_iterator it = player->_inventory.begin(); it != player->_inventory.end(); ++it) {
+ Common::String n((*it)->_name);
+ n.toLowercase();
+
+ if (t.contains(n)) {
+ if ((*it)->_value < enemy->_rejectsOffers) {
+ appendText("Your offer is rejected.");
+ } else {
+ appendText("Your offer is accepted.");
+ appendText(enemy->_acceptsOfferComment.c_str());
+ _world->move(*it, enemy);
+ _world->move(enemy, _world->_storageScene);
+ }
+
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+bool WageEngine::tryAttack(const Obj *weapon, const Common::String &input) {
+ Common::String w(weapon->_name);
+ w.toLowercase();
+ Common::String i(input);
+ i.toLowercase();
+ Common::String v(weapon->_operativeVerb);
+ v.toLowercase();
+
+ return i.contains(w) && i.contains(v);
+}
+
+bool WageEngine::handleAttack(Obj *weapon) {
+ Chr *player = _world->_player;
+ Chr *enemy = getMonster();
+
+ if (weapon->_type == Obj::MAGICAL_OBJECT) {
+ switch (weapon->_attackType) {
+ case Obj::HEALS_PHYSICAL_AND_SPIRITUAL_DAMAGE:
+ case Obj::HEALS_PHYSICAL_DAMAGE:
+ case Obj::HEALS_SPIRITUAL_DAMAGE:
+ performMagic(player, enemy, weapon);
+ return true;
+ }
+ }
+ if (enemy != NULL)
+ performAttack(player, enemy, weapon);
+ else if (weapon->_type == Obj::MAGICAL_OBJECT)
+ appendText("There is nobody to cast a spell at.");
+ else
+ appendText("There is no one to fight.");
+
+ return true;
+}
+
+const char *WageEngine::getPercentMessage(double percent) {
+ if (percent < 0.40) {
+ return "very bad";
+ } else if (percent < 0.55) {
+ return "bad";
+ } else if (percent < 0.70) {
+ return "average";
+ } else if (percent < 0.85) {
+ return "good";
+ } else if (percent <= 1.00) {
+ return "very good";
+ } else {
+ return "enhanced";
+ }
+}
+
+void WageEngine::printPlayerCondition(Chr *player) {
+ double physicalPercent = (double)player->_context._statVariables[PHYS_HIT_CUR] / player->_context._statVariables[PHYS_HIT_BAS];
+ double spiritualPercent = (double)player->_context._statVariables[SPIR_HIT_CUR] / player->_context._statVariables[SPIR_HIT_BAS];
+ char buf[256];
+
+ snprintf(buf, 256, "Your physical condition is %s.", getPercentMessage(physicalPercent));
+ appendText(buf);
+
+ snprintf(buf, 256, "Your spiritual condition is %s.", getPercentMessage(spiritualPercent));
+ appendText(buf);
+}
+
+} // End of namespace Wage
diff --git a/engines/wage/configure.engine b/engines/wage/configure.engine
new file mode 100644
index 0000000000..6205211158
--- /dev/null
+++ b/engines/wage/configure.engine
@@ -0,0 +1,3 @@
+# This file is included from the main "configure" script
+# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps]
+add_engine wage "WAGE" no
diff --git a/engines/wage/debugger.cpp b/engines/wage/debugger.cpp
new file mode 100644
index 0000000000..7d01b0b85e
--- /dev/null
+++ b/engines/wage/debugger.cpp
@@ -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.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 "wage/wage.h"
+#include "wage/debugger.h"
+#include "wage/entities.h"
+#include "wage/script.h"
+#include "wage/world.h"
+
+namespace Wage {
+
+Debugger::Debugger(WageEngine *engine) : GUI::Debugger(), _engine(engine) {
+ registerCmd("continue", WRAP_METHOD(Debugger, cmdExit));
+ registerCmd("scenes", WRAP_METHOD(Debugger, Cmd_ListScenes));
+ registerCmd("script", WRAP_METHOD(Debugger, Cmd_Script));
+}
+
+Debugger::~Debugger() {
+}
+
+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;
+}
+
+bool Debugger::Cmd_ListScenes(int argc, const char **argv) {
+ int currentScene;
+
+ for (uint i = 1; i < _engine->_world->_orderedScenes.size(); i++) { // #0 is STORAGE@
+ if (_engine->_world->_player->_currentScene == _engine->_world->_orderedScenes[i])
+ currentScene = i;
+
+ debugPrintf("%d: %s\n", i, _engine->_world->_orderedScenes[i]->_name.c_str());
+ }
+
+ debugPrintf("\nCurrent scene is #%d: %s\n", currentScene, _engine->_world->_orderedScenes[currentScene]->_name.c_str());
+
+ return true;
+}
+
+bool Debugger::Cmd_Script(int argc, const char **argv) {
+ Script *script = _engine->_world->_player->_currentScene->_script;
+
+ if (argc >= 2) {
+ int scriptNum = strToInt(argv[1]);
+
+ if (scriptNum)
+ script = _engine->_world->_orderedScenes[scriptNum]->_script;
+ else
+ script = _engine->_world->_globalScript;
+ }
+
+ if (script == NULL) {
+ debugPrintf("There is no script for current scene\n");
+ return true;
+ }
+
+ for (uint i = 0; i < script->_scriptText.size(); i++) {
+ debugPrintf("%d [%04x]: %s\n", i, script->_scriptText[i]->offset, script->_scriptText[i]->line.c_str());
+ }
+
+ return true;
+}
+
+} // End of namespace Wage
diff --git a/engines/wage/debugger.h b/engines/wage/debugger.h
new file mode 100644
index 0000000000..90687760cb
--- /dev/null
+++ b/engines/wage/debugger.h
@@ -0,0 +1,47 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 WAGE_DEBUGGER_H
+#define WAGE_DEBUGGER_H
+
+#include "common/scummsys.h"
+#include "gui/debugger.h"
+
+namespace Wage {
+
+class WageEngine;
+
+class Debugger : public GUI::Debugger {
+protected:
+ WageEngine *_engine;
+
+ bool Cmd_ListScenes(int argc, const char **argv);
+ bool Cmd_Script(int argc, const char **argv);
+
+public:
+ Debugger(WageEngine *engine);
+ virtual ~Debugger();
+};
+
+} // End of namespace Wage
+
+#endif
diff --git a/engines/wage/design.cpp b/engines/wage/design.cpp
new file mode 100644
index 0000000000..907a1ec435
--- /dev/null
+++ b/engines/wage/design.cpp
@@ -0,0 +1,599 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * MIT License:
+ *
+ * Copyright (c) 2009 Alexei Svitkine, Eugene Sandulenko
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include "graphics/primitives.h"
+#include "wage/wage.h"
+#include "wage/design.h"
+
+namespace Wage {
+
+struct PlotData {
+ Graphics::Surface *surface;
+ Patterns *patterns;
+ uint fillType;
+ int thickness;
+ Design *design;
+
+ PlotData(Graphics::Surface *s, Patterns *p, int f, int t, Design *d) :
+ surface(s), patterns(p), fillType(f), thickness(t), design(d) {}
+};
+
+void drawPixel(int x, int y, int color, void *data);
+void drawPixelPlain(int x, int y, int color, void *data);
+
+Design::Design(Common::SeekableReadStream *data) {
+ _len = data->readUint16BE() - 2;
+ _data = (byte *)malloc(_len);
+ data->read(_data, _len);
+
+ _surface = NULL;
+ _bounds = NULL;
+
+ _boundsCalculationMode = false;
+}
+
+Design::~Design() {
+ free(_data);
+ if (_surface)
+ _surface->free();
+ delete _surface;
+}
+
+void Design::paint(Graphics::Surface *surface, Patterns &patterns, int x, int y) {
+ bool needRender = false;
+
+ if (_surface == NULL) {
+ _boundsCalculationMode = true;
+ _bounds->debugPrint(4, "Internal bounds:");
+ render(patterns);
+ _boundsCalculationMode = false;
+ if (_bounds->right == -10000) {
+ _bounds->left = _bounds->top = _bounds->right = _bounds->bottom = 0;
+ }
+ _bounds->debugPrint(4, "Calculated bounds:");
+
+ _surface = new Graphics::Surface;
+ _surface->create(_bounds->width(), _bounds->height(), Graphics::PixelFormat::createFormatCLUT8());
+
+ Common::Rect r(0, 0, _bounds->width(), _bounds->height());
+ _surface->fillRect(r, kColorGreen);
+
+ needRender = true;
+ }
+
+ _bounds->debugPrint(4, "Using bounds:");
+#if 0
+ PlotData pd(_surface, &patterns, 8, 1, this);
+ int x1 = 50, y1 = 50, x2 = 200, y2 = 200, borderThickness = 30;
+ Common::Rect inn(x1-5, y1-5, x2+5, y2+5);
+ drawRoundRect(inn, 6, kColorGray, false, drawPixelPlain, &pd);
+
+ drawThickLine(x1, y1, x2-borderThickness, y1, borderThickness, kColorBlack, drawPixel, &pd);
+ drawThickLine(x2-borderThickness, y1, x2-borderThickness, y2, borderThickness, kColorBlack, drawPixel, &pd);
+ drawThickLine(x2-borderThickness, y2-borderThickness, x1, y2-borderThickness, borderThickness, kColorBlack, drawPixel, &pd);
+ drawThickLine(x1, y2-borderThickness, x1, y1, borderThickness, kColorBlack, drawPixel, &pd);
+ drawThickLine(x2+10, y2+10, x2+100, y2+100, borderThickness, kColorBlack, drawPixel, &pd);
+
+ g_system->copyRectToScreen(_surface->getPixels(), _surface->pitch, 0, 0, _surface->w, _surface->h);
+
+ while (true) {
+ ((WageEngine *)g_engine)->processEvents();
+ g_system->updateScreen();
+ g_system->delayMillis(50);
+ }
+ return;
+#endif
+
+ if (needRender)
+ render(patterns);
+
+ if (_bounds->width() && _bounds->height()) {
+ const int padding = 3;
+ for (int i = padding; i < _bounds->height() - 2 * padding; i++) {
+ const byte *src = (const byte *)_surface->getBasePtr(padding, i);
+ byte *dst = (byte *)surface->getBasePtr(x + padding, y+i);
+ for (int j = padding; j < _bounds->width() - 2 * padding; j++) {
+ if (*src != kColorGreen)
+ *dst = *src;
+ src++;
+ dst++;
+ }
+ }
+ }
+}
+
+void Design::render(Patterns &patterns) {
+ Common::MemoryReadStream in(_data, _len);
+ bool needRender = true;
+
+ while (needRender) {
+ byte fillType = in.readByte();
+ byte borderThickness = in.readByte();
+ byte borderFillType = in.readByte();
+ int type = in.readByte();
+
+ if (in.eos())
+ break;
+
+ debug(8, "fill: %d borderFill: %d border: %d type: %d", fillType, borderFillType, borderThickness, type);
+ switch (type) {
+ case 4:
+ drawRect(_surface, in, patterns, fillType, borderThickness, borderFillType);
+ break;
+ case 8:
+ drawRoundRect(_surface, in, patterns, fillType, borderThickness, borderFillType);
+ break;
+ case 12:
+ drawOval(_surface, in, patterns, fillType, borderThickness, borderFillType);
+ break;
+ case 16:
+ case 20:
+ drawPolygon(_surface, in, patterns, fillType, borderThickness, borderFillType);
+ break;
+ case 24:
+ drawBitmap(_surface, in);
+ break;
+ default:
+ warning("Unknown type => %d", type);
+ break;
+ }
+
+ //g_system->copyRectToScreen(_surface->getPixels(), _surface->pitch, 0, 0, _surface->w, _surface->h);
+ //((WageEngine *)g_engine)->processEvents();
+ //g_system->updateScreen();
+ //g_system->delayMillis(500);
+ }
+}
+
+bool Design::isPointOpaque(int x, int y) {
+ if (_surface == NULL)
+ error("Surface is null");
+
+ byte pixel = ((byte *)_surface->getBasePtr(x, y))[0];
+
+ return pixel != kColorGreen;
+}
+
+void Design::adjustBounds(int16 x, int16 y) {
+ _bounds->right = MAX(x, _bounds->right);
+ _bounds->bottom = MAX(y, _bounds->bottom);
+}
+
+void drawPixel(int x, int y, int color, void *data) {
+ PlotData *p = (PlotData *)data;
+
+ if (p->fillType > p->patterns->size())
+ return;
+
+ if (p->design && p->design->isBoundsCalculation()) {
+ if (x < 0 || y < 0)
+ return;
+ if (p->thickness == 1) {
+ p->design->adjustBounds(x, y);
+ } else {
+ int x1 = x - p->thickness / 2;
+ int x2 = x1 + p->thickness;
+ int y1 = y - p->thickness / 2;
+ int y2 = y1 + p->thickness;
+
+ for (y = y1; y < y2; y++)
+ for (x = x1; x < x2; x++)
+ p->design->adjustBounds(x, y);
+ }
+
+ return;
+ }
+
+ byte *pat = p->patterns->operator[](p->fillType - 1);
+
+ if (p->thickness == 1) {
+ if (x >= 0 && x < p->surface->w && y >= 0 && y < p->surface->h) {
+ uint xu = (uint)x; // for letting compiler optimize it
+ uint yu = (uint)y;
+
+ *((byte *)p->surface->getBasePtr(xu, yu)) =
+ (pat[yu % 8] & (1 << (7 - xu % 8))) ?
+ color : kColorWhite;
+ }
+ } else {
+ int x1 = x - p->thickness / 2;
+ int x2 = x1 + p->thickness;
+ int y1 = y - p->thickness / 2;
+ int y2 = y1 + p->thickness;
+
+ for (y = y1; y < y2; y++)
+ for (x = x1; x < x2; x++)
+ if (x >= 0 && x < p->surface->w && y >= 0 && y < p->surface->h) {
+ uint xu = (uint)x; // for letting compiler optimize it
+ uint yu = (uint)y;
+ *((byte *)p->surface->getBasePtr(xu, yu)) =
+ (pat[yu % 8] & (1 << (7 - xu % 8))) ?
+ color : kColorWhite;
+ }
+ }
+}
+
+void drawPixelPlain(int x, int y, int color, void *data) {
+ PlotData *p = (PlotData *)data;
+
+ if (p->design && p->design->isBoundsCalculation()) {
+ p->design->adjustBounds(x, y);
+ return;
+ }
+
+ if (x >= 0 && x < p->surface->w && y >= 0 && y < p->surface->h)
+ *((byte *)p->surface->getBasePtr(x, y)) = (byte)color;
+}
+
+void Design::drawRect(Graphics::Surface *surface, Common::ReadStream &in,
+ Patterns &patterns, byte fillType, byte borderThickness, byte borderFillType) {
+ int16 y1 = in.readSint16BE();
+ int16 x1 = in.readSint16BE();
+ int16 y2 = in.readSint16BE();
+ int16 x2 = in.readSint16BE();
+
+ if (x1 > x2)
+ SWAP(x1, x2);
+ if (y1 > y2)
+ SWAP(y1, y2);
+
+ Common::Rect r(x1, y1, x2, y2);
+ PlotData pd(surface, &patterns, fillType, 1, this);
+
+ if (fillType <= patterns.size())
+ Graphics::drawFilledRect(r, kColorBlack, drawPixel, &pd);
+
+ pd.fillType = borderFillType;
+ pd.thickness = borderThickness;
+
+ if (borderThickness > 0 && borderFillType <= patterns.size()) {
+ Graphics::drawLine(x1, y1, x2, y1, kColorBlack, drawPixel, &pd);
+ Graphics::drawLine(x2, y1, x2, y2, kColorBlack, drawPixel, &pd);
+ Graphics::drawLine(x2, y2, x1, y2, kColorBlack, drawPixel, &pd);
+ Graphics::drawLine(x1, y2, x1, y1, kColorBlack, drawPixel, &pd);
+ }
+}
+
+void Design::drawRoundRect(Graphics::Surface *surface, Common::ReadStream &in,
+ Patterns &patterns, byte fillType, byte borderThickness, byte borderFillType) {
+ int16 y1 = in.readSint16BE();
+ int16 x1 = in.readSint16BE();
+ int16 y2 = in.readSint16BE();
+ int16 x2 = in.readSint16BE();
+ int16 arc = in.readSint16BE();
+
+ if (x1 > x2)
+ SWAP(x1, x2);
+ if (y1 > y2)
+ SWAP(y1, y2);
+
+ Common::Rect r(x1, y1, x2, y2);
+ PlotData pd(surface, &patterns, fillType, 1, this);
+
+ if (fillType <= patterns.size())
+ Graphics::drawRoundRect(r, arc / 2, kColorBlack, true, drawPixel, &pd);
+
+ pd.fillType = borderFillType;
+ pd.thickness = borderThickness;
+
+ if (borderThickness > 0 && borderFillType <= patterns.size())
+ Graphics::drawRoundRect(r, arc / 2, kColorBlack, false, drawPixel, &pd);
+}
+
+void Design::drawPolygon(Graphics::Surface *surface, Common::ReadStream &in,
+ Patterns &patterns, byte fillType, byte borderThickness, byte borderFillType) {
+
+ byte ignored = in.readSint16BE(); // ignored
+
+ if (ignored)
+ warning("Ignored: %d", ignored);
+
+ int numBytes = in.readSint16BE(); // #bytes used by polygon data, including the numBytes
+ int16 by1 = in.readSint16BE();
+ int16 bx1 = in.readSint16BE();
+ int16 by2 = in.readSint16BE();
+ int16 bx2 = in.readSint16BE();
+ Common::Rect bbox(bx1, by1, bx2, by2);
+
+ numBytes -= 8;
+
+ int y1 = in.readSint16BE();
+ int x1 = in.readSint16BE();
+
+ Common::Array<int> xcoords;
+ Common::Array<int> ycoords;
+
+ numBytes -= 6;
+
+ while (numBytes > 0) {
+ int y2 = y1;
+ int x2 = x1;
+ int b = in.readSByte();
+ if (b == -128) {
+ y2 = in.readSint16BE();
+ numBytes -= 3;
+ } else {
+ y2 += b;
+ numBytes -= 1;
+ }
+ b = in.readSByte();
+ if (b == -128) {
+ x2 = in.readSint16BE();
+ numBytes -= 3;
+ } else {
+ x2 += b;
+ numBytes -= 1;
+ }
+ xcoords.push_back(x1);
+ ycoords.push_back(y1);
+ x1 = x2;
+ y1 = y2;
+ }
+ xcoords.push_back(x1);
+ ycoords.push_back(y1);
+
+ int npoints = xcoords.size();
+ int *xpoints = (int *)calloc(npoints, sizeof(int));
+ int *ypoints = (int *)calloc(npoints, sizeof(int));
+ for (int i = 0; i < npoints; i++) {
+ xpoints[i] = xcoords[i];
+ ypoints[i] = ycoords[i];
+ }
+
+ PlotData pd(surface, &patterns, fillType, 1, this);
+
+ if (fillType <= patterns.size()) {
+ Graphics::drawPolygonScan(xpoints, ypoints, npoints, bbox, kColorBlack, drawPixel, &pd);
+ }
+
+ pd.fillType = borderFillType;
+ pd.thickness = borderThickness;
+ if (borderThickness > 0 && borderFillType <= patterns.size()) {
+ for (int i = 1; i < npoints; i++)
+ Graphics::drawLine(xpoints[i-1], ypoints[i-1], xpoints[i], ypoints[i], kColorBlack, drawPixel, &pd);
+ }
+
+ free(xpoints);
+ free(ypoints);
+}
+
+void Design::drawOval(Graphics::Surface *surface, Common::ReadStream &in,
+ Patterns &patterns, byte fillType, byte borderThickness, byte borderFillType) {
+ int16 y1 = in.readSint16BE();
+ int16 x1 = in.readSint16BE();
+ int16 y2 = in.readSint16BE();
+ int16 x2 = in.readSint16BE();
+ PlotData pd(surface, &patterns, fillType, 1, this);
+
+ if (fillType <= patterns.size())
+ Graphics::drawEllipse(x1, y1, x2-1, y2-1, kColorBlack, true, drawPixel, &pd);
+
+ pd.fillType = borderFillType;
+ pd.thickness = borderThickness;
+
+ if (borderThickness > 0 && borderFillType <= patterns.size())
+ Graphics::drawEllipse(x1, y1, x2-1, y2-1, kColorBlack, false, drawPixel, &pd);
+}
+
+void Design::drawBitmap(Graphics::Surface *surface, Common::SeekableReadStream &in) {
+ int numBytes = in.readSint16BE();
+ int y1 = in.readSint16BE();
+ int x1 = in.readSint16BE();
+ int y2 = in.readSint16BE();
+ int x2 = in.readSint16BE();
+ int w = x2 - x1;
+ int h = y2 - y1;
+ Graphics::Surface tmp;
+
+ tmp.create(w, h, Graphics::PixelFormat::createFormatCLUT8());
+
+ numBytes -= 10;
+
+ int x = 0, y = 0;
+ while (numBytes > 0 && y < h) {
+ int n = in.readSByte();
+ int count;
+ int b = 0;
+ int state = 0;
+
+ numBytes--;
+
+ if ((n >= 0) && (n <= 127)) { // If n is between 0 and 127 inclusive, copy the next n+1 bytes literally.
+ count = n + 1;
+ state = 1;
+ } else if ((n >= -127) && (n <= -1)) { // Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times.
+ b = in.readByte();
+ numBytes--;
+ count = -n + 1;
+ state = 2;
+ } else { // Else if n is -128, noop.
+ count = 0;
+ }
+
+ for (int i = 0; i < count && y < h; i++) {
+ byte color = 0;
+ if (state == 1) {
+ color = in.readByte();
+ numBytes--;
+ } else if (state == 2)
+ color = b;
+
+ for (int c = 0; c < 8; c++) {
+ if (_boundsCalculationMode) {
+ adjustBounds(x1 + x, y1 + y);
+ } else if (x1 + x >= 0 && x1 + x < surface->w && y1 + y >= 0 && y1 + y < surface->h)
+ *((byte *)tmp.getBasePtr(x, y)) = (color & (1 << (7 - c % 8))) ? kColorBlack : kColorWhite;
+ x++;
+ if (x == w) {
+ y++;
+ x = 0;
+ break;
+ }
+ }
+ }
+ }
+
+ in.skip(numBytes);
+
+ if (_boundsCalculationMode)
+ return;
+
+ FloodFill ff(&tmp, kColorWhite, kColorGreen);
+ for (int yy = 0; yy < h; yy++) {
+ ff.addSeed(0, yy);
+ ff.addSeed(w - 1, yy);
+ }
+ for (int xx = 0; xx < w; xx++) {
+ ff.addSeed(xx, 0);
+ ff.addSeed(xx, h - 1);
+ }
+ ff.fill();
+
+ for (y = 0; y < h && y1 + y < surface->h; y++) {
+ byte *src = (byte *)tmp.getBasePtr(0, y);
+ byte *dst = (byte *)surface->getBasePtr(x1, y1 + y);
+ for (x = 0; x < w; x++) {
+ if (*src != kColorGreen)
+ *dst = *src;
+ src++;
+ dst++;
+ }
+ }
+
+ tmp.free();
+}
+
+void Design::drawRect(Graphics::Surface *surface, Common::Rect &rect, int thickness, int color, Patterns &patterns, byte fillType) {
+ drawRect(surface, rect.left, rect.top, rect.right, rect.bottom, thickness, color, patterns, fillType);
+}
+
+void Design::drawRect(Graphics::Surface *surface, int x1, int y1, int x2, int y2, int thickness, int color, Patterns &patterns, byte fillType) {
+ PlotData pd(surface, &patterns, fillType, thickness, nullptr);
+
+ Graphics::drawLine(x1, y1, x2, y1, kColorBlack, drawPixel, &pd);
+ Graphics::drawLine(x2, y1, x2, y2, kColorBlack, drawPixel, &pd);
+ Graphics::drawLine(x2, y2, x1, y2, kColorBlack, drawPixel, &pd);
+ Graphics::drawLine(x1, y2, x1, y1, kColorBlack, drawPixel, &pd);
+}
+
+
+void Design::drawFilledRect(Graphics::Surface *surface, Common::Rect &rect, int color, Patterns &patterns, byte fillType) {
+ PlotData pd(surface, &patterns, fillType, 1, nullptr);
+
+ for (int y = rect.top; y <= rect.bottom; y++)
+ Graphics::drawHLine(rect.left, rect.right, y, color, drawPixel, &pd);
+}
+
+void Design::drawFilledRoundRect(Graphics::Surface *surface, Common::Rect &rect, int arc, int color, Patterns &patterns, byte fillType) {
+ PlotData pd(surface, &patterns, fillType, 1, nullptr);
+
+ Graphics::drawRoundRect(rect, arc, color, true, drawPixel, &pd);
+}
+
+void Design::drawHLine(Graphics::Surface *surface, int x1, int x2, int y, int thickness, int color, Patterns &patterns, byte fillType) {
+ PlotData pd(surface, &patterns, fillType, thickness, nullptr);
+
+ Graphics::drawHLine(x1, x2, y, color, drawPixel, &pd);
+}
+
+void Design::drawVLine(Graphics::Surface *surface, int x, int y1, int y2, int thickness, int color, Patterns &patterns, byte fillType) {
+ PlotData pd(surface, &patterns, fillType, thickness, nullptr);
+
+ Graphics::drawVLine(x, y1, y2, color, drawPixel, &pd);
+}
+
+FloodFill::FloodFill(Graphics::Surface *surface, byte color1, byte color2) {
+ _surface = surface;
+ _color1 = color1;
+ _color2 = color2;
+ _w = surface->w;
+ _h = surface->h;
+
+ _visited = (byte *)calloc(_w * _h, 1);
+}
+
+FloodFill::~FloodFill() {
+ while(!_queue.empty()) {
+ Common::Point *p = _queue.front();
+
+ delete p;
+ _queue.pop_front();
+ }
+
+ free(_visited);
+}
+
+void FloodFill::addSeed(int x, int y) {
+ byte *p;
+
+ if (x >= 0 && x < _w && y >= 0 && y < _h) {
+ if (!_visited[y * _w + x] && *(p = (byte *)_surface->getBasePtr(x, y)) == _color1) {
+ _visited[y * _w + x] = 1;
+ *p = _color2;
+
+ Common::Point *pt = new Common::Point(x, y);
+
+ _queue.push_back(pt);
+ }
+ }
+}
+
+void FloodFill::fill() {
+ while (!_queue.empty()) {
+ Common::Point *p = _queue.front();
+ _queue.pop_front();
+ addSeed(p->x , p->y - 1);
+ addSeed(p->x - 1, p->y );
+ addSeed(p->x , p->y + 1);
+ addSeed(p->x + 1, p->y );
+
+ delete p;
+ }
+}
+
+
+} // End of namespace Wage
diff --git a/engines/wage/design.h b/engines/wage/design.h
new file mode 100644
index 0000000000..e8f42f4e04
--- /dev/null
+++ b/engines/wage/design.h
@@ -0,0 +1,119 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * MIT License:
+ *
+ * Copyright (c) 2009 Alexei Svitkine, Eugene Sandulenko
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#ifndef WAGE_DESIGN_H
+#define WAGE_DESIGN_H
+
+#include "graphics/surface.h"
+#include "common/memstream.h"
+#include "common/rect.h"
+
+namespace Wage {
+
+class Design {
+public:
+ Design(Common::SeekableReadStream *data);
+ ~Design();
+
+ void setBounds(Common::Rect *bounds) {
+ _bounds = bounds;
+ }
+
+ Common::Rect *getBounds() {
+ return _bounds;
+ }
+
+ void paint(Graphics::Surface *canvas, Patterns &patterns, int x, int y);
+ bool isPointOpaque(int x, int y);
+ static void drawRect(Graphics::Surface *surface, Common::Rect &rect, int thickness, int color, Patterns &patterns, byte fillType);
+ static void drawRect(Graphics::Surface *surface, int x1, int y1, int x2, int y2, int thickness, int color, Patterns &patterns, byte fillType);
+ static void drawFilledRect(Graphics::Surface *surface, Common::Rect &rect, int color, Patterns &patterns, byte fillType);
+ static void drawFilledRoundRect(Graphics::Surface *surface, Common::Rect &rect, int arc, int color, Patterns &patterns, byte fillType);
+ static void drawHLine(Graphics::Surface *surface, int x1, int x2, int y, int thickness, int color, Patterns &patterns, byte fillType);
+ static void drawVLine(Graphics::Surface *surface, int x, int y1, int y2, int thickness, int color, Patterns &patterns, byte fillType);
+
+ bool isBoundsCalculation() { return _boundsCalculationMode; }
+ void adjustBounds(int16 x, int16 y);
+
+private:
+ byte *_data;
+ int _len;
+ Common::Rect *_bounds;
+ Graphics::Surface *_surface;
+ bool _boundsCalculationMode;
+
+private:
+ void render(Patterns &patterns);
+ void drawRect(Graphics::Surface *surface, Common::ReadStream &in,
+ Patterns &patterns, byte fillType, byte borderThickness, byte borderFillType);
+ void drawRoundRect(Graphics::Surface *surface, Common::ReadStream &in,
+ Patterns &patterns, byte fillType, byte borderThickness, byte borderFillType);
+ void drawPolygon(Graphics::Surface *surface, Common::ReadStream &in,
+ Patterns &patterns, byte fillType, byte borderThickness, byte borderFillType);
+ void drawOval(Graphics::Surface *surface, Common::ReadStream &in,
+ Patterns &patterns, byte fillType, byte borderThickness, byte borderFillType);
+ void drawBitmap(Graphics::Surface *surface, Common::SeekableReadStream &in);
+};
+
+class FloodFill {
+public:
+ FloodFill(Graphics::Surface *surface, byte color1, byte color2);
+ ~FloodFill();
+ void addSeed(int x, int y);
+ void fill();
+
+private:
+ Common::List<Common::Point *> _queue;
+ Graphics::Surface *_surface;
+ byte _color1, _color2;
+ byte *_visited;
+ int _w, _h;
+};
+
+} // End of namespace Wage
+
+#endif
diff --git a/engines/wage/detection.cpp b/engines/wage/detection.cpp
new file mode 100644
index 0000000000..512d432e54
--- /dev/null
+++ b/engines/wage/detection.cpp
@@ -0,0 +1,151 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public 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/system.h"
+#include "common/savefile.h"
+
+#include "wage/wage.h"
+
+namespace Wage {
+
+const char *WageEngine::getGameFile() const {
+ return _gameDescription->filesDescriptions[0].fileName;
+}
+
+}
+
+static const PlainGameDescriptor wageGames[] = {
+ {"afm", "Another Fine Mess"},
+ {"amot", "A Mess O' Trouble"},
+ {"cantitoe", "Camp Cantitoe"},
+ {"drakmythcastle", "Drakmyth Castle"},
+ {"raysmaze", "Ray's Maze"},
+ {"scepters", "Enchanted Scepters"},
+ {"twisted", "Twisted!"},
+ {"wage", "WAGE"},
+ {0, 0}
+};
+
+#include "wage/detection_tables.h"
+
+class WageMetaEngine : public AdvancedMetaEngine {
+public:
+ WageMetaEngine() : AdvancedMetaEngine(Wage::gameDescriptions, sizeof(ADGameDescription), wageGames) {
+ _singleId = "wage";
+ _guiOptions = GUIO2(GUIO_NOSPEECH, GUIO_NOMIDI);
+ }
+
+ virtual const char *getName() const {
+ return "World Adventure Game Engine";
+ }
+
+ virtual const char *getOriginalCopyright() const {
+ return "World Builder (C) Silicon Beach Software";
+ }
+
+ virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
+ virtual bool hasFeature(MetaEngineFeature f) const;
+ virtual SaveStateList listSaves(const char *target) const;
+ virtual int getMaximumSaveSlot() const;
+ virtual void removeSaveState(const char *target, int slot) const;
+};
+
+bool WageMetaEngine::hasFeature(MetaEngineFeature f) const {
+ return
+ (f == kSupportsListSaves) ||
+ (f == kSupportsLoadingDuringStartup) ||
+ (f == kSupportsDeleteSave);
+}
+
+bool Wage::WageEngine::hasFeature(EngineFeature f) const {
+ return
+ (f == kSupportsRTL) ||
+ (f == kSupportsLoadingDuringRuntime) ||
+ (f == kSupportsSavingDuringRuntime);
+}
+
+bool WageMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const {
+ if (desc) {
+ *engine = new Wage::WageEngine(syst, desc);
+ }
+ return desc != 0;
+}
+
+SaveStateList WageMetaEngine::listSaves(const char *target) const {
+ const uint32 WAGEflag = MKTAG('W','A','G','E');
+ Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
+ Common::StringArray filenames;
+ char saveDesc[31];
+ Common::String pattern = target;
+ pattern += ".???";
+
+ filenames = saveFileMan->listSavefiles(pattern);
+
+ 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) {
+ Common::InSaveFile *in = saveFileMan->openForLoading(*file);
+ if (in) {
+ uint32 type = in->readUint32BE();
+ if (type == WAGEflag)
+ in->read(saveDesc, 31);
+ saveList.push_back(SaveStateDescriptor(slotNum, saveDesc));
+ delete in;
+ }
+ }
+ }
+
+ // Sort saves based on slot number.
+ Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator());
+ return saveList;
+}
+
+int WageMetaEngine::getMaximumSaveSlot() const { return 999; }
+
+void WageMetaEngine::removeSaveState(const char *target, int slot) const {
+ g_system->getSavefileManager()->removeSavefile(Common::String::format("%s.%03d", target, slot));
+}
+
+#if PLUGIN_ENABLED_DYNAMIC(WAGE)
+ REGISTER_PLUGIN_DYNAMIC(WAGE, PLUGIN_TYPE_ENGINE, WageMetaEngine);
+#else
+ REGISTER_PLUGIN_STATIC(WAGE, PLUGIN_TYPE_ENGINE, WageMetaEngine);
+#endif
+
+namespace Wage {
+
+bool WageEngine::canLoadGameStateCurrently() {
+ return false;
+}
+
+bool WageEngine::canSaveGameStateCurrently() {
+ return false;
+}
+
+} // End of namespace Wage
diff --git a/engines/wage/detection_tables.h b/engines/wage/detection_tables.h
new file mode 100644
index 0000000000..e164ea70cd
--- /dev/null
+++ b/engines/wage/detection_tables.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.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public 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 Wage {
+
+#define ADGF_DEFAULT (ADGF_DROPLANGUAGE|ADGF_DROPPLATFORM|ADGF_MACRESFORK)
+#define ADGF_GENERIC (ADGF_DEFAULT|ADGF_USEEXTRAASTITLE|ADGF_AUTOGENTARGET)
+#define ADGF_DEMO (ADGF_GENERIC|ADGF_DEMO)
+
+#define FANGAME(n,m,s) { "wage",n,AD_ENTRY1s(n,m,s),Common::EN_ANY,Common::kPlatformMacintosh,ADGF_GENERIC,GUIO0()}
+#define FANGAMEN(n,f,m,s) { "wage",n,AD_ENTRY1s(f,m,s),Common::EN_ANY,Common::kPlatformMacintosh,ADGF_GENERIC,GUIO0()}
+#define FANGAMEND(n,f,m,s) { "wage",n,AD_ENTRY1s(f,m,s),Common::EN_ANY,Common::kPlatformMacintosh,ADGF_DEMO,GUIO0()}
+#define BIGGAME(t,v,f,m,s) { t,v,AD_ENTRY1s(f,m,s),Common::EN_ANY,Common::kPlatformMacintosh,ADGF_DEFAULT,GUIO0()}
+
+static const ADGameDescription gameDescriptions[] = {
+ FANGAME("3rd Floor", "913812a1ac7a6b0e48dadd1afa1c7763", 281409),
+ BIGGAME("afm", "v1.8", "Another Fine Mess 1.8", "94a9c4f8b3dabd1846d76215a49bd221", 1420723),
+ BIGGAME("amot", "v1.8", "A Mess O' Trouble 1.8", "26207bdf0bb539464f136f0669af885f", 1843104),
+ FANGAME("Bug Hunt", "595117cbed33e8de1ab3714b33880205", 195699),
+ BIGGAME("cantitoe", "", "Camp Cantitoe", "913812a1ac7a6b0e48dadd1afa1c7763", 616985),
+ // Problems with letter rendering
+ FANGAME("Canal District", "a56aa3cd4a6e070e15ce1d5815c7be0a", 641470),
+ FANGAME("Carbon Copy", "913812a1ac7a6b0e48dadd1afa1c7763", 519445),
+ // Invalid rect in scene "FINALE"
+ FANGAME("Castle of Ert", "327610eb2298a9427a566288312df040", 198955),
+ FANGAME("Deep Angst", "b130b3c811cd89024dd5fdd2b71f70b8", 329550),
+ FANGAME("Deep Ennui", "913812a1ac7a6b0e48dadd1afa1c7763", 86075),
+ // Polygons with ignored byte 1
+ FANGAME("Double Trouble", "1652e36857a04c01dc560234c4818619", 542371),
+ BIGGAME("drakmythcastle", "disk I", "Drakmyth Castle disk I of II", "94a9c4f8b3dabd1846d76215a49bd221", 793784),
+ BIGGAME("drakmythcastle", "disk II", "Drakmyth Castle II", "cc978cc9a5256724702463cb5aaaffa0", 1685659),
+ // Crash at start in GUI rendering
+ FANGAME("Dune Eternity", "94a9c4f8b3dabd1846d76215a49bd221", 290201), // Original file name is "***DUNE ETERNITY*** "
+ FANGAMEN("Dungeon World II", "DungeonWorld2", "0154ea11d3cbb536c13b4ae9e6902d48", 230199),
+ FANGAME("Eidisi I", "595117cbed33e8de1ab3714b33880205", 172552),
+ // Problems(?) with text on the first screen
+ FANGAMEN("Enchanted Pencils", "Enchanted Pencils 0.99 (PG)", "595117cbed33e8de1ab3714b33880205", 408913),
+ FANGAME("Escape from School!", "913812a1ac7a6b0e48dadd1afa1c7763", 50105),
+ FANGAME("Exploration Zeta!", "c477921aeee6ed0f8997ba44447eb2d0", 366599),
+ // Crash in console rendering on the first scene
+ FANGAME("Fantasy Quest", "4b0e1a1fbaaa4930accd0f9f0e1519c7", 762754),
+ FANGAME("Find the Heart", "595117cbed33e8de1ab3714b33880205", 106235), // From Joshua's Worlds 1.0
+ // Problems with window overlay
+ FANGAMEN("Jumble", "LSJUMBLE", "e12ec4d76d48bdc86567c5e63750547e", 647339), // Original file name is "LSJUMBLE† "
+ FANGAME("Karth of the Jungle", "595117cbed33e8de1ab3714b33880205", 96711),
+ FANGAME("Karth of the Jungle", "595117cbed33e8de1ab3714b33880205", 96960), // Alternative version
+ FANGAME("Karth of the Jungle II", "c106835ab4436de054e03aec3ce904ce", 201053),
+ FANGAMEN("Little Pythagoras", "Little Pythagoras 1.1.1", "94a9c4f8b3dabd1846d76215a49bd221", 628821),
+ FANGAME("Lost Crystal", "8174c81ea1858d0079ae040dae2cefd3", 771072),
+ FANGAME("Magic Rings", "913812a1ac7a6b0e48dadd1afa1c7763", 109044),
+ // No way to click on the house
+ FANGAME("Messy House", "913812a1ac7a6b0e48dadd1afa1c7763", 177120),
+ FANGAME("Midnight Snack", "913812a1ac7a6b0e48dadd1afa1c7763", 67952),
+ FANGAME("Midnight Snack", "913812a1ac7a6b0e48dadd1afa1c7763", 67966), // Alt version
+ FANGAME("Minitorian", "913812a1ac7a6b0e48dadd1afa1c7763", 586464),
+ // No way to pass through the first screen
+ FANGAME("Nightcrawler Ned", "94a9c4f8b3dabd1846d76215a49bd221", 366542),
+ FANGAME("Pavilion", "4d991d7d1534d48d90598d86ea6d5d97", 231687),
+ // Polygons with byte 1
+ FANGAME("Periapt", "913812a1ac7a6b0e48dadd1afa1c7763", 406006),
+ FANGAME("Puzzle Piece Search", "595117cbed33e8de1ab3714b33880205", 247693), // From Joshua's Worlds 1.0
+ // Empty(?) first scene
+ FANGAME("Pyramid of No Return", "77a55a45f794b4d4a56703d3acce871e", 385145),
+ FANGAME("Queen Quest", "4b0e1a1fbaaa4930accd0f9f0e1519c7", 57026),
+ FANGAME("Quest for T-Rex", "913812a1ac7a6b0e48dadd1afa1c7763", 592584),
+ // Crash in console rendering on the initial scene
+ FANGAME("Quest for the Dark Sword", "b35dd0c078da9f35fc25a455f56bb129", 572576),
+ FANGAME("Radical Castle", "677bfee4afeca2f7152eb8b76c85ca8d", 355601),
+ FANGAME("Radical Castle 1.0", "677bfee4afeca2f7152eb8b76c85ca8d", 347278),
+ BIGGAME("raysmaze", "v1.5", "Ray's Maze1.5", "064b16d8c20724f8debbbdc3aafde538", 1408516),
+ BIGGAME("raysmaze", "v1.5/alt", "Ray's Maze1.5", "92cca777800c3d31a77b5ed7f6ee49ad", 1408516),
+ BIGGAME("scepters", "", "Scepters", "3311deef8bf82f0b4b1cfa15a3b3289d", 346595),
+ // ??? problems with dog bitmap?
+ FANGAMEN("Space Adventure", "SpaceAdventure", "f9f3f1c419f56955f7966355b34ea5c8", 155356),
+ FANGAMEN("Spear of Destiny", "SpearOfDestiny", "913812a1ac7a6b0e48dadd1afa1c7763", 333665), // Original file name "SpearOfDestiny†"
+ FANGAME("Star Trek", "44aaef4806578700429de5aaf95c266e", 53320),
+ FANGAME("Strange Disappearance", "d81f2d03a1e863f04fb1e3a5495b720e", 772282),
+ // Code 0x03 in text
+ FANGAME("Swamp Witch", "913812a1ac7a6b0e48dadd1afa1c7763", 739781), // Original file name "Swamp Witch†"
+ FANGAME("Sweetspace Now!", "e12ec4d76d48bdc86567c5e63750547e", 123813), // Comes with Jumble
+ FANGAME("Time Bomb", "4b0e1a1fbaaa4930accd0f9f0e1519c7", 64564),
+ FANGAME("Time Bomb", "4b0e1a1fbaaa4930accd0f9f0e1519c7", 64578), // Alt version
+ FANGAMEND("The Ashland Revolution", "The Ashland Revolution Demo", "913812a1ac7a6b0e48dadd1afa1c7763", 145023), // Original file name "The Ashland Revolution Demo†"
+ FANGAMEN("The Hotel Caper", "The Hotel Caper V1.0", "595117cbed33e8de1ab3714b33880205", 231969),
+ // Invalid rect in scene "Access Tube 1"
+ FANGAMEN("The Phoenix v1.2", "The Phoenix", "4b0e1a1fbaaa4930accd0f9f0e1519c7", 431640),
+ FANGAME("The Sultan's Palace", "358799d446ee4fc12f793febd6c94b95", 456855),
+ // Admission for on 3rd screen is messed up
+ FANGAME("The Tower", "435f420b9dff895ae1ddf1338040c51d", 556539),
+ // Polygons with ignored byte 1 and 2 on second scene
+ FANGAME("The Village", "913812a1ac7a6b0e48dadd1afa1c7763", 314828),
+ // Doesn't go past first scene
+ BIGGAME("twisted", "", "Twisted! 1.6", "26207bdf0bb539464f136f0669af885f", 960954),
+ FANGAME("Wishing Well", "913812a1ac7a6b0e48dadd1afa1c7763", 103688),
+ FANGAME("Wizard's Warehouse", "913812a1ac7a6b0e48dadd1afa1c7763", 159748),
+ FANGAME("ZikTuria", "418e74ca71029a1e9db80d0eb30c0843", 52972),
+ FANGAME("Zoony", "539a64151426edc92da5eedadf39f23c", 154990), // original filename "Zoony™"
+
+ AD_TABLE_END_MARKER
+};
+
+} // End of namespace Wage
diff --git a/engines/wage/dialog.cpp b/engines/wage/dialog.cpp
new file mode 100644
index 0000000000..263570bddc
--- /dev/null
+++ b/engines/wage/dialog.cpp
@@ -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.
+ *
+ * MIT License:
+ *
+ * Copyright (c) 2009 Alexei Svitkine, Eugene Sandulenko
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include "common/system.h"
+#include "common/events.h"
+
+#include "wage/wage.h"
+#include "wage/design.h"
+#include "wage/gui.h"
+#include "wage/dialog.h"
+
+namespace Wage {
+
+enum {
+ kDialogHeight = 113
+};
+
+Dialog::Dialog(Gui *gui, int width, const char *text, DialogButtonArray *buttons, uint defaultButton) :
+ _gui(gui), _text(text), _buttons(buttons), _defaultButton(defaultButton) {
+ assert(_gui->_engine);
+ assert(_gui->_engine->_world);
+
+ _font = getDialogFont();
+
+ _tempSurface.create(width + 1, kDialogHeight + 1, Graphics::PixelFormat::createFormatCLUT8());
+
+ _bbox.left = (_gui->_screen.w - width) / 2;
+ _bbox.top = (_gui->_screen.h - kDialogHeight) / 2;
+ _bbox.right = (_gui->_screen.w + width) / 2;
+ _bbox.bottom = (_gui->_screen.h + kDialogHeight) / 2;
+
+ _pressedButton = -1;
+
+ _mouseOverPressedButton = false;
+
+ // Adjust button positions
+ for (uint i = 0; i < _buttons->size(); i++)
+ _buttons->operator[](i)->bounds.translate(_bbox.left, _bbox.top);
+
+ _needsRedraw = true;
+}
+
+Dialog::~Dialog() {
+}
+
+const Graphics::Font *Dialog::getDialogFont() {
+ return _gui->getFont("Chicago-12", Graphics::FontManager::kBigGUIFont);
+}
+
+void Dialog::paint() {
+ Design::drawFilledRect(&_gui->_screen, _bbox, kColorWhite, _gui->_patterns, kPatternSolid);
+ _font->drawString(&_gui->_screen, _text, _bbox.left + 24, _bbox.top + 16, _bbox.width(), kColorBlack);
+
+ static int boxOutline[] = { 1, 0, 0, 1, 1 };
+ drawOutline(_bbox, boxOutline, ARRAYSIZE(boxOutline));
+
+ for (uint i = 0; i < _buttons->size(); i++) {
+ DialogButton *button = _buttons->operator[](i);
+ static int buttonOutline[] = { 0, 0, 0, 0, 1 };
+
+ if (i == _defaultButton) {
+ buttonOutline[0] = buttonOutline[1] = 1;
+ } else {
+ buttonOutline[0] = buttonOutline[1] = 0;
+ }
+
+ int color = kColorBlack;
+
+ if ((int)i == _pressedButton && _mouseOverPressedButton) {
+ Common::Rect bb(button->bounds.left + 5, button->bounds.top + 5,
+ button->bounds.right - 5, button->bounds.bottom - 5);
+
+ Design::drawFilledRect(&_gui->_screen, bb, kColorBlack, _gui->_patterns, kPatternSolid);
+
+ color = kColorWhite;
+ }
+ int w = _font->getStringWidth(button->text);
+ int x = button->bounds.left + (button->bounds.width() - w) / 2;
+ int y = button->bounds.top + 6;
+
+ _font->drawString(&_gui->_screen, button->text, x, y, _bbox.width(), color);
+
+ drawOutline(button->bounds, buttonOutline, ARRAYSIZE(buttonOutline));
+ }
+
+ g_system->copyRectToScreen(_gui->_screen.getBasePtr(_bbox.left, _bbox.top), _gui->_screen.pitch,
+ _bbox.left, _bbox.top, _bbox.width() + 1, _bbox.height() + 1);
+
+ _needsRedraw = false;
+}
+
+void Dialog::drawOutline(Common::Rect &bounds, int *spec, int speclen) {
+ for (int i = 0; i < speclen; i++)
+ if (spec[i] != 0)
+ Design::drawRect(&_gui->_screen, bounds.left + i, bounds.top + i, bounds.right - i, bounds.bottom - i,
+ 1, kColorBlack, _gui->_patterns, kPatternSolid);
+}
+
+int Dialog::run() {
+ bool shouldQuit = false;
+ Common::Rect r(_bbox);
+
+ _tempSurface.copyRectToSurface(_gui->_screen.getBasePtr(_bbox.left, _bbox.top), _gui->_screen.pitch, 0, 0, _bbox.width() + 1, _bbox.height() + 1);
+ _gui->pushArrowCursor();
+
+ while (!shouldQuit) {
+ Common::Event event;
+
+ while (_gui->_engine->_eventMan->pollEvent(event)) {
+ switch (event.type) {
+ case Common::EVENT_QUIT:
+ _gui->_engine->_shouldQuit = true;
+ shouldQuit = true;
+ break;
+ case Common::EVENT_MOUSEMOVE:
+ mouseMove(event.mouse.x, event.mouse.y);
+ break;
+ case Common::EVENT_LBUTTONDOWN:
+ mouseClick(event.mouse.x, event.mouse.y);
+ break;
+ case Common::EVENT_LBUTTONUP:
+ shouldQuit = mouseRaise(event.mouse.x, event.mouse.y);
+ break;
+ case Common::EVENT_KEYDOWN:
+ switch (event.kbd.keycode) {
+ case Common::KEYCODE_ESCAPE:
+ _pressedButton = -1;
+ shouldQuit = true;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (_needsRedraw)
+ paint();
+
+ g_system->updateScreen();
+ g_system->delayMillis(50);
+ }
+
+ _gui->_screen.copyRectToSurface(_tempSurface.getBasePtr(0, 0), _tempSurface.pitch, _bbox.left, _bbox.top, _bbox.width() + 1, _bbox.height() + 1);
+ g_system->copyRectToScreen(_gui->_screen.getBasePtr(r.left, r.top), _gui->_screen.pitch, r.left, r.top, r.width() + 1, r.height() + 1);
+
+ _gui->popCursor();
+
+ return _pressedButton;
+}
+
+int Dialog::matchButton(int x, int y) {
+ for (uint i = 0; i < _buttons->size(); i++)
+ if (_buttons->operator[](i)->bounds.contains(x, y))
+ return i;
+
+ return -1;
+}
+
+void Dialog::mouseMove(int x, int y) {
+ if (_pressedButton != -1) {
+ int match = matchButton(x, y);
+
+ if (_mouseOverPressedButton && match != _pressedButton) {
+ _mouseOverPressedButton = false;
+ _needsRedraw = true;
+ } else if (!_mouseOverPressedButton && match == _pressedButton) {
+ _mouseOverPressedButton = true;
+ _needsRedraw = true;
+ }
+ }
+}
+
+void Dialog::mouseClick(int x, int y) {
+ int match = matchButton(x, y);
+
+ if (match != -1) {
+ _pressedButton = match;
+ _mouseOverPressedButton = true;
+
+ _needsRedraw = true;
+ }
+}
+
+int Dialog::mouseRaise(int x, int y) {
+ bool res = false;
+
+ if (_pressedButton != -1) {
+ if (matchButton(x, y) == _pressedButton)
+ res = true;
+ }
+
+ return res;
+}
+
+} // End of namespace Wage
diff --git a/engines/wage/dialog.h b/engines/wage/dialog.h
new file mode 100644
index 0000000000..c5878acc95
--- /dev/null
+++ b/engines/wage/dialog.h
@@ -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.
+ *
+ * MIT License:
+ *
+ * Copyright (c) 2009 Alexei Svitkine, Eugene Sandulenko
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#ifndef WAGE_DIALOG_H
+#define WAGE_DIALOG_H
+
+namespace Wage {
+
+struct DialogButton {
+ Common::String text;
+ Common::Rect bounds;
+
+ DialogButton(const char *t, int x1, int y1, int w, int h) {
+ text = t;
+ bounds.left = x1;
+ bounds.top = y1;
+ bounds.right = x1 + w - 1;
+ bounds.bottom = y1 + h - 1;
+ }
+};
+
+typedef Common::Array<DialogButton *> DialogButtonArray;
+
+class Dialog {
+public:
+ Dialog(Gui *gui, int width, const char *text, DialogButtonArray *buttons, uint defaultButton);
+ ~Dialog();
+
+ int run();
+
+private:
+ Gui *_gui;
+ Graphics::Surface _tempSurface;
+ Common::Rect _bbox;
+ Common::String _text;
+
+ const Graphics::Font *_font;
+ DialogButtonArray *_buttons;
+ int _pressedButton;
+ uint _defaultButton;
+ bool _mouseOverPressedButton;
+
+ bool _needsRedraw;
+
+private:
+ const Graphics::Font *getDialogFont();
+ void drawOutline(Common::Rect &bounds, int *spec, int speclen);
+ void paint();
+ void mouseMove(int x, int y);
+ void mouseClick(int x, int y);
+ int mouseRaise(int x, int y);
+ int matchButton(int x, int y);
+};
+
+} // End of namespace Wage
+
+#endif
diff --git a/engines/wage/entities.cpp b/engines/wage/entities.cpp
new file mode 100644
index 0000000000..a2648c49fe
--- /dev/null
+++ b/engines/wage/entities.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.
+ *
+ * MIT License:
+ *
+ * Copyright (c) 2009 Alexei Svitkine, Eugene Sandulenko
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include "wage/wage.h"
+#include "wage/entities.h"
+#include "wage/design.h"
+#include "wage/script.h"
+#include "wage/world.h"
+
+#include "common/memstream.h"
+
+namespace Wage {
+
+void Designed::setDesignBounds(Common::Rect *bounds) {
+ _designBounds = bounds;
+ _design->setBounds(bounds);
+}
+
+Designed::~Designed() {
+ delete _design;
+ delete _designBounds;
+}
+
+Context::Context() {
+ _visits = 0;
+ _kills = 0;
+ _experience = 0;
+ _frozen = false;
+
+ for (int i = 0; i < 26 * 9; i++)
+ _userVariables[i] = 0;
+
+ for (int i = 0; i < 18; i++)
+ _statVariables[i] = 0;
+}
+
+Scene::Scene() {
+ _script = NULL;
+ _design = NULL;
+ _textBounds = NULL;
+ _fontSize = 0;
+ _fontType = 0;
+
+ for (int i = 0; i < 4; i++)
+ _blocked[i] = false;
+
+ _soundFrequency = 0;
+ _soundType = 0;
+ _worldX = 0;
+ _worldY = 0;
+
+ _visited = false;
+}
+
+Scene::Scene(Common::String name, Common::SeekableReadStream *data) {
+ debug(9, "Creating scene: %s", name.c_str());
+
+ _name = name;
+ _classType = SCENE;
+ _design = new Design(data);
+
+ _script = NULL;
+ _textBounds = NULL;
+ _fontSize = 0;
+ _fontType = 0;
+
+ setDesignBounds(readRect(data));
+ _worldY = data->readSint16BE();
+ _worldX = data->readSint16BE();
+ _blocked[NORTH] = (data->readByte() != 0);
+ _blocked[SOUTH] = (data->readByte() != 0);
+ _blocked[EAST] = (data->readByte() != 0);
+ _blocked[WEST] = (data->readByte() != 0);
+ _soundFrequency = data->readSint16BE();
+ _soundType = data->readByte();
+ data->readByte(); // unknown
+ _messages[NORTH] = readPascalString(data);
+ _messages[SOUTH] = readPascalString(data);
+ _messages[EAST] = readPascalString(data);
+ _messages[WEST] = readPascalString(data);
+ _soundName = readPascalString(data);
+
+ _visited = false;
+
+ delete data;
+}
+
+Scene::~Scene() {
+ delete _script;
+ delete _textBounds;
+}
+
+void Scene::paint(Graphics::Surface *surface, int x, int y) {
+ Common::Rect r(x + 5, y + 5, _design->getBounds()->width() + x - 10, _design->getBounds()->height() + y - 10);
+ surface->fillRect(r, kColorWhite);
+
+ _design->paint(surface, ((WageEngine *)g_engine)->_world->_patterns, x, y);
+
+ for (ObjList::const_iterator it = _objs.begin(); it != _objs.end(); ++it) {
+ debug(2, "paining Obj: %s", (*it)->_name.c_str());
+ (*it)->_design->paint(surface, ((WageEngine *)g_engine)->_world->_patterns, x, y);
+ }
+
+ for (ChrList::const_iterator it = _chrs.begin(); it != _chrs.end(); ++it) {
+ debug(2, "paining Chr: %s", (*it)->_name.c_str());
+ (*it)->_design->paint(surface, ((WageEngine *)g_engine)->_world->_patterns, x, y);
+ }
+}
+
+// Source: Apple IIGS Technical Note #41, "Font Family Numbers"
+// http://apple2.boldt.ca/?page=til/tn.iigs.041
+static const char *const fontNames[] = {
+ "Chicago", // system font
+ "Geneva", // application font
+ "New York",
+ "Geneva",
+
+ "Monaco",
+ "Venice",
+ "London",
+ "Athens",
+
+ "San Francisco",
+ "Toronto",
+ NULL,
+ "Cairo",
+ "Los Angeles", // 12
+
+ "Zapf Dingbats",
+ "Bookman",
+ "Helvetica Narrow",
+ "Palatino",
+ NULL,
+ "Zapf Chancery",
+ NULL,
+
+ "Times", // 20
+ "Helvetica",
+ "Courier",
+ "Symbol",
+ "Taliesin", // mobile?
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL, // 30
+ NULL,
+ NULL,
+ "Avant Garde",
+ "New Century Schoolbook"
+};
+
+const char *Scene::getFontName() {
+ if (_fontType >= 0 && _fontType < ARRAYSIZE(fontNames) && fontNames[_fontType] != NULL) {
+ return fontNames[_fontType];
+ }
+ return "Unknown";
+}
+
+Obj::Obj() : _currentOwner(NULL), _currentScene(NULL) {
+ _index = 0;
+ _namePlural = false;
+ _value = 0;
+ _attackType = 0;
+ _numberOfUses = 0;
+ _returnToRandomScene = false;
+ _type = 0;
+ _accuracy = 0;
+ _damage = 0;
+}
+
+Obj::Obj(Common::String name, Common::SeekableReadStream *data) {
+ _name = name;
+ _classType = OBJ;
+ _currentOwner = NULL;
+ _currentScene = NULL;
+
+ _index = 0;
+
+ _design = new Design(data);
+
+ setDesignBounds(readRect(data));
+
+ int16 namePlural = data->readSint16BE();
+
+ if (namePlural == 256)
+ _namePlural = true; // TODO: other flags?
+ else if (namePlural == 0)
+ _namePlural = false;
+ else
+ error("Obj <%s> had weird namePlural set (%d)", name.c_str(), namePlural);
+
+ if (data->readSint16BE() != 0)
+ error("Obj <%s> had short set", name.c_str());
+
+ if (data->readByte() != 0)
+ error("Obj <%s> had byte set", name.c_str());
+
+ _accuracy = data->readByte();
+ _value = data->readByte();
+ _type = data->readSByte();
+ _damage = data->readByte();
+ _attackType = data->readSByte();
+ _numberOfUses = data->readSint16BE();
+ int16 returnTo = data->readSint16BE();
+ if (returnTo == 256) // TODO any other possibilities?
+ _returnToRandomScene = true;
+ else if (returnTo == 0)
+ _returnToRandomScene = false;
+ else
+ error("Obj <%s> had weird returnTo set", name.c_str());
+
+ _sceneOrOwner = readPascalString(data);
+ _clickMessage = readPascalString(data);
+ _operativeVerb = readPascalString(data);
+ _failureMessage = readPascalString(data);
+ _useMessage = readPascalString(data);
+ _sound = readPascalString(data);
+
+ delete data;
+}
+
+Obj::~Obj() {
+}
+
+Chr *Obj::removeFromChr() {
+ if (_currentOwner != NULL) {
+ for (int i = (int)_currentOwner->_inventory.size() - 1; i >= 0; i--)
+ if (_currentOwner->_inventory[i] == this)
+ _currentOwner->_inventory.remove_at(i);
+
+ for (int i = 0; i < Chr::NUMBER_OF_ARMOR_TYPES; i++) {
+ if (_currentOwner->_armor[i] == this) {
+ _currentOwner->_armor[i] = NULL;
+ }
+ }
+ }
+
+ return _currentOwner;
+}
+
+Designed *Obj::removeFromCharOrScene() {
+ Designed *from = removeFromChr();
+
+ if (_currentScene != NULL) {
+ _currentScene->_objs.remove(this);
+ from = _currentScene;
+ }
+
+ return from;
+}
+
+void Obj::resetState(Chr *owner, Scene *scene) {
+ warning("STUB: Obj::resetState()");
+}
+
+Chr::Chr(Common::String name, Common::SeekableReadStream *data) {
+ _name = name;
+ _classType = CHR;
+ _design = new Design(data);
+
+ _index = 0;
+ _currentScene = NULL;
+
+ setDesignBounds(readRect(data));
+
+ _physicalStrength = data->readByte();
+ _physicalHp = data->readByte();
+ _naturalArmor = data->readByte();
+ _physicalAccuracy = data->readByte();
+
+ _spiritualStength = data->readByte();
+ _spiritialHp = data->readByte();
+ _resistanceToMagic = data->readByte();
+ _spiritualAccuracy = data->readByte();
+
+ _runningSpeed = data->readByte();
+ _rejectsOffers = data->readByte();
+ _followsOpponent = data->readByte();
+
+ data->readSByte(); // TODO: ???
+ data->readSint32BE(); // TODO: ???
+
+ _weaponDamage1 = data->readByte();
+ _weaponDamage2 = data->readByte();
+
+ data->readSByte(); // TODO: ???
+
+ if (data->readSByte() == 1)
+ _playerCharacter = true;
+ else
+ _playerCharacter = false;
+
+ _maximumCarriedObjects = data->readByte();
+ _returnTo = data->readSByte();
+
+ _winningWeapons = data->readByte();
+ _winningMagic = data->readByte();
+ _winningRun = data->readByte();
+ _winningOffer = data->readByte();
+ _losingWeapons = data->readByte();
+ _losingMagic = data->readByte();
+ _losingRun = data->readByte();
+ _losingOffer = data->readByte();
+
+ _gender = data->readSByte();
+ if (data->readSByte() == 1)
+ _nameProperNoun = true;
+ else
+ _nameProperNoun = false;
+
+ _initialScene = readPascalString(data);
+ _nativeWeapon1 = readPascalString(data);
+ _operativeVerb1 = readPascalString(data);
+ _nativeWeapon2 = readPascalString(data);
+ _operativeVerb2 = readPascalString(data);
+
+ _initialComment = readPascalString(data);
+ _scoresHitComment = readPascalString(data);
+ _receivesHitComment = readPascalString(data);
+ _makesOfferComment = readPascalString(data);
+ _rejectsOfferComment = readPascalString(data);
+ _acceptsOfferComment = readPascalString(data);
+ _dyingWords = readPascalString(data);
+
+ _initialSound = readPascalString(data);
+ _scoresHitSound = readPascalString(data);
+ _receivesHitSound = readPascalString(data);
+ _dyingSound = readPascalString(data);
+
+ _weaponSound1 = readPascalString(data);
+ _weaponSound2 = readPascalString(data);
+
+ for (int i = 0; i < NUMBER_OF_ARMOR_TYPES; i++)
+ _armor[i] = NULL;
+
+ _weapon1 = NULL;
+ _weapon2 = NULL;
+
+ // Create native weapons
+ if (!_nativeWeapon1.empty() && !_operativeVerb1.empty()) {
+ _weapon1 = new Obj;
+
+ _weapon1->_name = _nativeWeapon1;
+ _weapon1->_operativeVerb = _operativeVerb1;
+ _weapon1->_type = Obj::REGULAR_WEAPON;
+ _weapon1->_accuracy = 0;
+ _weapon1->_damage = _weaponDamage1;
+ _weapon1->_sound = _weaponSound1;
+ }
+
+ if (!_nativeWeapon2.empty() && !_operativeVerb2.empty()) {
+ _weapon2 = new Obj;
+
+ _weapon2->_name = _nativeWeapon2;
+ _weapon2->_operativeVerb = _operativeVerb2;
+ _weapon2->_type = Obj::REGULAR_WEAPON;
+ _weapon2->_accuracy = 0;
+ _weapon2->_damage = _weaponDamage2;
+ _weapon2->_sound = _weaponSound2;
+ }
+
+ delete data;
+}
+
+void Chr::resetState() {
+ _context._statVariables[PHYS_STR_BAS] = _context._statVariables[PHYS_STR_CUR] = _physicalStrength;
+ _context._statVariables[PHYS_HIT_BAS] = _context._statVariables[PHYS_HIT_CUR] = _physicalHp;
+ _context._statVariables[PHYS_ARM_BAS] = _context._statVariables[PHYS_ARM_CUR] = _naturalArmor;
+ _context._statVariables[PHYS_ACC_BAS] = _context._statVariables[PHYS_ACC_CUR] = _physicalAccuracy;
+
+ _context._statVariables[SPIR_STR_BAS] = _context._statVariables[SPIR_STR_CUR] = _spiritualStength;
+ _context._statVariables[SPIR_HIT_BAS] = _context._statVariables[SPIR_HIT_CUR] = _spiritialHp;
+ _context._statVariables[SPIR_ARM_BAS] = _context._statVariables[SPIR_ARM_CUR] = _naturalArmor;
+ _context._statVariables[SPIR_ACC_BAS] = _context._statVariables[SPIR_ACC_CUR] = _physicalAccuracy;
+
+ _context._statVariables[PHYS_SPE_BAS] = _context._statVariables[PHYS_SPE_CUR] = _runningSpeed;
+}
+
+ObjArray *Chr::getWeapons(bool includeMagic) {
+ ObjArray *list = new ObjArray;
+
+ if (_weapon1)
+ list->push_back(_weapon1);
+
+ if (_weapon2)
+ list->push_back(_weapon2);
+
+ for (uint i = 0; i < _inventory.size(); i++)
+ switch (_inventory[i]->_type) {
+ case Obj::REGULAR_WEAPON:
+ case Obj::THROW_WEAPON:
+ list->push_back(_inventory[i]);
+ break;
+ case Obj::MAGICAL_OBJECT:
+ if (includeMagic)
+ list->push_back(_inventory[i]);
+ break;
+ default:
+ break;
+ }
+
+ return list;
+}
+
+ObjArray *Chr::getMagicalObjects() {
+ ObjArray *list = new ObjArray;
+
+ for (uint i = 0; i < _inventory.size(); i++)
+ if (_inventory[i]->_type == Obj::MAGICAL_OBJECT)
+ list->push_back(_inventory[i]);
+
+ return list;
+}
+
+void Chr::wearObjs() {
+ for (uint i = 0; i < _inventory.size(); i++)
+ wearObjIfPossible(_inventory[i]);
+}
+
+int Chr::wearObjIfPossible(Obj *obj) {
+ switch (obj->_type) {
+ case Obj::HELMET:
+ if (_armor[HEAD_ARMOR] == NULL) {
+ _armor[HEAD_ARMOR] = obj;
+ return Chr::HEAD_ARMOR;
+ }
+ break;
+ case Obj::CHEST_ARMOR:
+ if (_armor[BODY_ARMOR] == NULL) {
+ _armor[BODY_ARMOR] = obj;
+ return Chr::BODY_ARMOR;
+ }
+ break;
+ case Obj::SHIELD:
+ if (_armor[SHIELD_ARMOR] == NULL) {
+ _armor[SHIELD_ARMOR] = obj;
+ return Chr::SHIELD_ARMOR;
+ }
+ break;
+ case Obj::SPIRITUAL_ARMOR:
+ if (_armor[MAGIC_ARMOR] == NULL) {
+ _armor[MAGIC_ARMOR] = obj;
+ return Chr::MAGIC_ARMOR;
+ }
+ break;
+ default:
+ return -1;
+ }
+
+ return -1;
+}
+
+const char *Chr::getDefiniteArticle(bool capitalize) {
+ if (!_nameProperNoun)
+ return capitalize ? "The " : "the ";
+
+ return "";
+}
+
+bool Chr::isWearing(Obj *obj) {
+ for (int i = 0; i < NUMBER_OF_ARMOR_TYPES; i++)
+ if (_armor[i] == obj)
+ return true;
+
+ return false;
+}
+
+} // End of namespace Wage
diff --git a/engines/wage/entities.h b/engines/wage/entities.h
new file mode 100644
index 0000000000..33cf087322
--- /dev/null
+++ b/engines/wage/entities.h
@@ -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.
+ *
+ * MIT License:
+ *
+ * Copyright (c) 2009 Alexei Svitkine, Eugene Sandulenko
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#ifndef WAGE_ENTITIES_H
+#define WAGE_ENTITIES_H
+
+namespace Graphics {
+ struct Surface;
+}
+
+namespace Wage {
+
+class Design;
+class Script;
+
+enum StatVariable {
+/** The base physical accuracy of the player. */
+ PHYS_ACC_BAS = 0,
+/** The current physical accuracy of the player. */
+ PHYS_ACC_CUR = 1,
+/** The base physical armor of the player. */
+ PHYS_ARM_BAS = 2,
+/** The current physical armor of the player. */
+ PHYS_ARM_CUR = 3,
+/** The base physical hit points of the player. */
+ PHYS_HIT_BAS = 4,
+/** The current physical hit points of the player. */
+ PHYS_HIT_CUR = 5,
+/** The base physical speed of the player. */
+ PHYS_SPE_BAS = 6,
+/** The current physical speed of the player. */
+ PHYS_SPE_CUR = 7,
+/** The base physical strength of the player. */
+ PHYS_STR_BAS = 8,
+/** The current physical strength of the player. */
+ PHYS_STR_CUR = 9,
+/** The base spiritual accuracy of the player. */
+ SPIR_ACC_BAS = 10,
+/** The current spiritual accuracy of the player. */
+ SPIR_ACC_CUR = 11,
+/** The base spiritual armor of the player. */
+ SPIR_ARM_BAS = 12,
+/** The current spiritual armor of the player. */
+ SPIR_ARM_CUR = 13,
+/** The base spiritual hit points of the player. */
+ SPIR_HIT_BAS = 14,
+/** The current spiritual hit points of the player. */
+ SPIR_HIT_CUR = 15,
+/** The base spiritual strength of the player. */
+ SPIR_STR_BAS = 16,
+/** The current spiritual strength of the player. */
+ SPIR_STR_CUR = 17
+};
+
+class Context {
+public:
+ Context();
+
+ int16 _visits; // Number of scenes visited, including repeated visits
+ int16 _kills; // Number of characters killed
+ int16 _experience;
+ bool _frozen;
+ int16 _userVariables[26 * 9];
+ int16 _statVariables[18];
+};
+
+class Designed {
+public:
+ Designed() : _design(NULL), _designBounds(NULL), _classType(UNKNOWN) {}
+ ~Designed();
+
+ Common::String _name;
+ Design *_design;
+ Common::Rect *_designBounds;
+ OperandType _classType;
+
+ Common::Rect *getDesignBounds() {
+ return _designBounds == NULL ? NULL : new Common::Rect(*_designBounds);
+ }
+
+ void setDesignBounds(Common::Rect *bounds);
+
+ Common::String toString() { if (!this) return "<NULL>"; return _name; }
+};
+
+class Chr : public Designed {
+public:
+ enum ChrDestination {
+ RETURN_TO_STORAGE = 0,
+ RETURN_TO_RANDOM_SCENE = 1,
+ RETURN_TO_INITIAL_SCENE = 2
+ };
+
+ enum ChrPart {
+ HEAD = 0,
+ CHEST = 1,
+ SIDE = 2
+ };
+
+ enum ChrArmorType {
+ HEAD_ARMOR = 0,
+ BODY_ARMOR = 1,
+ SHIELD_ARMOR = 2,
+ MAGIC_ARMOR = 3,
+ NUMBER_OF_ARMOR_TYPES = 4
+ };
+
+ Chr(Common::String name, Common::SeekableReadStream *data);
+
+ int _index;
+ Common::String _initialScene;
+ int _gender;
+ bool _nameProperNoun;
+ bool _playerCharacter;
+ uint _maximumCarriedObjects;
+ int _returnTo;
+
+ int _physicalStrength;
+ int _physicalHp;
+ int _naturalArmor;
+ int _physicalAccuracy;
+ int _spiritualStength;
+ int _spiritialHp;
+ int _resistanceToMagic;
+ int _spiritualAccuracy;
+ int _runningSpeed;
+ uint _rejectsOffers;
+ int _followsOpponent;
+
+ Common::String _initialSound;
+ Common::String _scoresHitSound;
+ Common::String _receivesHitSound;
+ Common::String _dyingSound;
+
+ Common::String _nativeWeapon1;
+ Common::String _operativeVerb1;
+ int _weaponDamage1;
+ Common::String _weaponSound1;
+
+ Common::String _nativeWeapon2;
+ Common::String _operativeVerb2;
+ int _weaponDamage2;
+ Common::String _weaponSound2;
+
+ int _winningWeapons;
+ int _winningMagic;
+ int _winningRun;
+ int _winningOffer;
+ int _losingWeapons;
+ int _losingMagic;
+ int _losingRun;
+ int _losingOffer;
+
+ Common::String _initialComment;
+ Common::String _scoresHitComment;
+ Common::String _receivesHitComment;
+ Common::String _makesOfferComment;
+ Common::String _rejectsOfferComment;
+ Common::String _acceptsOfferComment;
+ Common::String _dyingWords;
+
+ Scene *_currentScene;
+ ObjArray _inventory;
+
+ Obj *_armor[NUMBER_OF_ARMOR_TYPES];
+
+ Context _context;
+
+ ObjArray *getWeapons(bool includeMagic);
+ ObjArray *getMagicalObjects();
+ const char *getDefiniteArticle(bool capitalize);
+
+ Obj *_weapon1;
+ Obj *_weapon2;
+
+public:
+ int wearObjIfPossible(Obj *obj);
+ void wearObjs();
+
+ void resetState();
+
+ bool isWearing(Obj *obj);
+};
+
+class Obj : public Designed {
+public:
+ Obj();
+ Obj(Common::String name, Common::SeekableReadStream *data);
+ ~Obj();
+
+ enum ObjectType {
+ REGULAR_WEAPON = 1,
+ THROW_WEAPON = 2,
+ MAGICAL_OBJECT = 3,
+ HELMET = 4,
+ SHIELD = 5,
+ CHEST_ARMOR = 6,
+ SPIRITUAL_ARMOR = 7,
+ MOBILE_OBJECT = 8,
+ IMMOBILE_OBJECT = 9
+ };
+
+ enum AttackType {
+ CAUSES_PHYSICAL_DAMAGE = 0,
+ CAUSES_SPIRITUAL_DAMAGE = 1,
+ CAUSES_PHYSICAL_AND_SPIRITUAL_DAMAGE = 2,
+ HEALS_PHYSICAL_DAMAGE = 3,
+ HEALS_SPIRITUAL_DAMAGE = 4,
+ HEALS_PHYSICAL_AND_SPIRITUAL_DAMAGE = 5,
+ FREEZES_OPPONENT = 6
+ };
+
+public:
+ int _index;
+ bool _namePlural;
+ uint _value;
+ int _attackType;
+ int _numberOfUses;
+ bool _returnToRandomScene;
+ Common::String _sceneOrOwner;
+ Common::String _clickMessage;
+ Common::String _failureMessage;
+ Common::String _useMessage;
+
+ Scene *_currentScene;
+ Chr *_currentOwner;
+
+ int _type;
+ uint _accuracy;
+ Common::String _operativeVerb;
+ int _damage;
+ Common::String _sound;
+
+public:
+ void setCurrentOwner(Chr *currentOwner) {
+ _currentOwner = currentOwner;
+ if (currentOwner != NULL)
+ _currentScene = NULL;
+ }
+
+ void setCurrentScene(Scene *currentScene) {
+ _currentScene = currentScene;
+ if (currentScene != NULL)
+ _currentOwner = NULL;
+ }
+
+ Chr *removeFromChr();
+ Designed *removeFromCharOrScene();
+
+ void resetState(Chr *owner, Scene *scene);
+};
+
+class Scene : public Designed {
+public:
+ enum SceneTypes {
+ PERIODIC = 0,
+ RANDOM = 1
+ };
+
+ Script *_script;
+ Common::String _text;
+ Common::Rect *_textBounds;
+ int _fontSize;
+ int _fontType; // 3 => Geneva, 22 => Courier, param to TextFont() function
+ bool _blocked[4];
+ Common::String _messages[4];
+ int _soundFrequency; // times a minute, max 3600
+ int _soundType;
+ Common::String _soundName;
+ int _worldX;
+ int _worldY;
+ bool _visited;
+
+ ObjList _objs;
+ ChrList _chrs;
+
+ Scene();
+ Scene(Common::String name, Common::SeekableReadStream *data);
+ ~Scene();
+
+ Common::Rect *getTextBounds() {
+ return _textBounds == NULL ? NULL : new Common::Rect(*_textBounds);
+ }
+
+ void paint(Graphics::Surface *screen, int x, int y);
+
+ const char *getFontName();
+};
+
+} // End of namespace Wage
+
+#endif
diff --git a/engines/wage/gui-console.cpp b/engines/wage/gui-console.cpp
new file mode 100644
index 0000000000..ab5df637ec
--- /dev/null
+++ b/engines/wage/gui-console.cpp
@@ -0,0 +1,430 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * MIT License:
+ *
+ * Copyright (c) 2009 Alexei Svitkine, Eugene Sandulenko
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include "common/timer.h"
+#include "common/unzip.h"
+#include "graphics/cursorman.h"
+#include "graphics/fonts/bdf.h"
+#include "graphics/palette.h"
+
+#include "wage/wage.h"
+#include "wage/design.h"
+#include "wage/entities.h"
+#include "wage/menu.h"
+#include "wage/gui.h"
+#include "wage/world.h"
+
+namespace Wage {
+
+const Graphics::Font *Gui::getConsoleFont() {
+ char fontName[128];
+ Scene *scene = _engine->_world->_player->_currentScene;
+
+ snprintf(fontName, 128, "%s-%d", scene->getFontName(), scene->_fontSize);
+
+ return getFont(fontName, Graphics::FontManager::kConsoleFont);
+}
+
+void Gui::clearOutput() {
+ _out.clear();
+ _lines.clear();
+ _consoleFullRedraw = true;
+}
+
+void Gui::appendText(const char *s) {
+ Common::String str(s);
+ _consoleDirty = true;
+
+ if (!str.contains('\n')) {
+ _out.push_back(str);
+ flowText(str);
+ return;
+ }
+
+ // Okay, we got new lines, need to split it
+ // and push substrings individually
+ Common::String tmp;
+
+ for (uint i = 0; i < str.size(); i++) {
+ if (str[i] == '\n') {
+ _out.push_back(tmp);
+ flowText(tmp);
+ tmp.clear();
+ continue;
+ }
+
+ tmp += str[i];
+ }
+
+ _out.push_back(tmp);
+ flowText(tmp);
+}
+
+enum {
+ kConWOverlap = 20,
+ kConHOverlap = 20,
+ kConWPadding = 3,
+ kConHPadding = 4,
+ kConOverscan = 3
+};
+
+void Gui::flowText(Common::String &str) {
+ Common::StringArray wrappedLines;
+ int textW = _consoleTextArea.width() - kConWPadding * 2;
+ const Graphics::Font *font = getConsoleFont();
+
+ font->wordWrapText(str, textW, wrappedLines);
+
+ if (wrappedLines.empty()) // Sometimes we have empty lines
+ _lines.push_back("");
+
+ for (Common::StringArray::const_iterator j = wrappedLines.begin(); j != wrappedLines.end(); ++j)
+ _lines.push_back(*j);
+
+ uint pos = _scrollPos;
+ _scrollPos = MAX<int>(0, (_lines.size() - 1 - _consoleNumLines) * _consoleLineHeight);
+
+ _cursorX = kConWPadding;
+
+ if (_scrollPos)
+ _cursorY = (_consoleNumLines) * _consoleLineHeight + kConHPadding;
+ else
+ _cursorY = (_lines.size() - 1) * _consoleLineHeight + kConHPadding;
+
+ if (pos != _scrollPos)
+ _consoleFullRedraw = true;
+
+ if (!_engine->_temporarilyHidden)
+ draw();
+}
+
+void Gui::renderConsole(Graphics::Surface *g, Common::Rect &r) {
+ bool fullRedraw = _consoleFullRedraw;
+ bool textReflow = false;
+ int surfW = r.width() + kConWOverlap * 2;
+ int surfH = r.height() + kConHOverlap * 2;
+
+ Common::Rect boundsR(kConWOverlap - kConOverscan, kConHOverlap - kConOverscan,
+ r.width() + kConWOverlap + kConOverscan, r.height() + kConHOverlap + kConOverscan);
+ Common::Rect fullR(0, 0, surfW, surfH);
+
+ if (_console.w != surfW || _console.h != surfH) {
+ if (_console.w != surfW)
+ textReflow = true;
+
+ _console.free();
+
+ _console.create(surfW, surfH, Graphics::PixelFormat::createFormatCLUT8());
+ fullRedraw = true;
+ }
+
+ if (fullRedraw)
+ _console.fillRect(fullR, kColorWhite);
+
+ const Graphics::Font *font = getConsoleFont();
+
+ _consoleLineHeight = font->getFontHeight();
+ int textW = r.width() - kConWPadding * 2;
+ int textH = r.height() - kConHPadding * 2;
+
+ if (textReflow) {
+ _lines.clear();
+
+ for (uint i = 0; i < _out.size(); i++)
+ flowText(_out[i]);
+ }
+
+ const int firstLine = _scrollPos / _consoleLineHeight;
+ const int lastLine = MIN((_scrollPos + textH) / _consoleLineHeight + 1, _lines.size());
+ const int xOff = kConWOverlap;
+ const int yOff = kConHOverlap;
+ int x1 = xOff + kConWPadding;
+ int y1 = yOff - (_scrollPos % _consoleLineHeight) + kConHPadding;
+
+ if (fullRedraw)
+ _consoleNumLines = (r.height() - 2 * kConWPadding) / _consoleLineHeight - 2;
+
+ for (int line = firstLine; line < lastLine; line++) {
+ const char *str = _lines[line].c_str();
+ int color = kColorBlack;
+
+ if ((line > _selectionStartY && line < _selectionEndY) ||
+ (line > _selectionEndY && line < _selectionStartY)) {
+ color = kColorWhite;
+ Common::Rect trect(0, y1, _console.w, y1 + _consoleLineHeight);
+
+ Design::drawFilledRect(&_console, trect, kColorBlack, _patterns, kPatternSolid);
+ }
+
+ if (line == _selectionStartY || line == _selectionEndY) {
+ if (_selectionStartY != _selectionEndY) {
+ int color1 = kColorBlack;
+ int color2 = kColorWhite;
+ int midpoint = _selectionStartX;
+
+ if (_selectionStartY > _selectionEndY)
+ SWAP(color1, color2);
+
+ if (line == _selectionEndY) {
+ SWAP(color1, color2);
+ midpoint = _selectionEndX;
+ }
+
+ Common::String beg(_lines[line].c_str(), &_lines[line].c_str()[midpoint]);
+ Common::String end(&_lines[line].c_str()[midpoint]);
+
+ int rectW = font->getStringWidth(beg) + kConWPadding + kConWOverlap;
+ Common::Rect trect(0, y1, _console.w, y1 + _consoleLineHeight);
+ if (color1 == kColorWhite)
+ trect.right = rectW;
+ else
+ trect.left = rectW;
+
+ Design::drawFilledRect(&_console, trect, kColorBlack, _patterns, kPatternSolid);
+
+ font->drawString(&_console, beg, x1, y1, textW, color1);
+ font->drawString(&_console, end, x1 + rectW - kConWPadding - kConWOverlap, y1, textW, color2);
+ } else {
+ int startPos = _selectionStartX;
+ int endPos = _selectionEndX;
+
+ if (startPos > endPos)
+ SWAP(startPos, endPos);
+
+ Common::String beg(_lines[line].c_str(), &_lines[line].c_str()[startPos]);
+ Common::String mid(&_lines[line].c_str()[startPos], &_lines[line].c_str()[endPos]);
+ Common::String end(&_lines[line].c_str()[endPos]);
+
+ int rectW1 = font->getStringWidth(beg) + kConWPadding + kConWOverlap;
+ int rectW2 = rectW1 + font->getStringWidth(mid);
+ Common::Rect trect(rectW1, y1, rectW2, y1 + _consoleLineHeight);
+
+ Design::drawFilledRect(&_console, trect, kColorBlack, _patterns, kPatternSolid);
+
+ font->drawString(&_console, beg, x1, y1, textW, kColorBlack);
+ font->drawString(&_console, mid, x1 + rectW1 - kConWPadding - kConWOverlap, y1, textW, kColorWhite);
+ font->drawString(&_console, end, x1 + rectW2 - kConWPadding - kConWOverlap, y1, textW, kColorBlack);
+ }
+ } else {
+ if (*str)
+ font->drawString(&_console, _lines[line], x1, y1, textW, color);
+ }
+
+ y1 += _consoleLineHeight;
+ }
+
+ // Now we need to clip it to the screen
+ int xcon = r.left - kConOverscan;
+ int ycon = r.top - kConOverscan;
+ if (xcon < 0) {
+ boundsR.left -= xcon;
+ xcon = 0;
+ }
+ if (ycon < 0) {
+ boundsR.top -= ycon;
+ ycon = 0;
+ }
+ if (xcon + boundsR.width() >= g->w)
+ boundsR.right -= xcon + boundsR.width() - g->w;
+ if (ycon + boundsR.height() >= g->h)
+ boundsR.bottom -= ycon + boundsR.height() - g->h;
+
+ Common::Rect rr(r);
+ if (rr.right > _screen.w - 1)
+ rr.right = _screen.w - 1;
+ if (rr.bottom > _screen.h - 1)
+ rr.bottom = _screen.h - 1;
+
+ g->copyRectToSurface(_console, xcon, ycon, boundsR);
+ g_system->copyRectToScreen(g->getBasePtr(rr.left, rr.top), g->pitch, rr.left, rr.top, rr.width(), rr.height());
+}
+
+void Gui::drawInput() {
+ if (!_screen.getPixels())
+ return;
+
+ if (_sceneIsActive) {
+ _sceneIsActive = false;
+ _bordersDirty = true;
+ }
+
+ _out.pop_back();
+ _lines.pop_back();
+ appendText(_engine->_inputText.c_str());
+ _inputTextLineNum = _out.size() - 1;
+
+ const Graphics::Font *font = getConsoleFont();
+
+ if (_engine->_inputText.contains('\n')) {
+ _consoleDirty = true;
+ } else {
+ int x = kConWPadding + _consoleTextArea.left;
+ int y = _cursorY + _consoleTextArea.top;
+
+ Common::Rect r(x, y, x + _consoleTextArea.width() - kConWPadding, y + font->getFontHeight());
+ _screen.fillRect(r, kColorWhite);
+
+ undrawCursor();
+
+ font->drawString(&_screen, _out[_inputTextLineNum], x, y, _screen.w, kColorBlack);
+
+ g_system->copyRectToScreen(_screen.getBasePtr(x, y), _screen.pitch, x, y, _consoleTextArea.width(), font->getFontHeight());
+ }
+
+ _cursorX = font->getStringWidth(_out[_inputTextLineNum]) + kConHPadding;
+}
+
+void Gui::actionCopy() {
+ if (_selectionStartX == -1)
+ return;
+
+ int startX = _selectionStartX;
+ int startY = _selectionStartY;
+ int endX = _selectionEndX;
+ int endY = _selectionEndY;
+
+ if (startY > endY) {
+ SWAP(startX, endX);
+ SWAP(endX, endY);
+ }
+
+ _clipboard.clear();
+
+ for (int i = startY; i <= endY; i++) {
+ if (startY == endY) {
+ _clipboard = Common::String(&_lines[i].c_str()[startX], &_lines[i].c_str()[endX]);
+ break;
+ }
+
+ if (i == startY) {
+ _clipboard += &_lines[i].c_str()[startX];
+ _clipboard += '\n';
+ } else if (i == endY) {
+ _clipboard += Common::String(_lines[i].c_str(), &_lines[i].c_str()[endX]);
+ } else {
+ _clipboard += _lines[i];
+ _clipboard += '\n';
+ }
+ }
+
+ _menu->enableCommand(kMenuEdit, kMenuActionPaste, true);
+}
+
+void Gui::actionPaste() {
+ _undobuffer = _engine->_inputText;
+ _engine->_inputText += _clipboard;
+ drawInput();
+ _engine->_inputText = _out.back(); // Set last part of the multiline text
+
+ _menu->enableCommand(kMenuEdit, kMenuActionUndo, true);
+}
+
+void Gui::actionUndo() {
+ _engine->_inputText = _undobuffer;
+ drawInput();
+
+ _menu->enableCommand(kMenuEdit, kMenuActionUndo, false);
+}
+
+void Gui::actionClear() {
+ int startPos = _selectionStartX;
+ int endPos = _selectionEndX;
+
+ if (startPos > endPos)
+ SWAP(startPos, endPos);
+
+ Common::String beg(_lines[_selectionStartY].c_str(), &_lines[_selectionStartY].c_str()[startPos]);
+ Common::String end(&_lines[_selectionStartY].c_str()[endPos]);
+
+ _undobuffer = _engine->_inputText;
+ _engine->_inputText = beg + end;
+ drawInput();
+
+ _menu->enableCommand(kMenuEdit, kMenuActionUndo, true);
+
+ _selectionStartY = -1;
+ _selectionEndY = -1;
+}
+
+void Gui::actionCut() {
+ int startPos = _selectionStartX;
+ int endPos = _selectionEndX;
+
+ if (startPos > endPos)
+ SWAP(startPos, endPos);
+
+ Common::String beg(_lines[_selectionStartY].c_str(), &_lines[_selectionStartY].c_str()[startPos]);
+ Common::String mid(&_lines[_selectionStartY].c_str()[startPos], &_lines[_selectionStartY].c_str()[endPos]);
+ Common::String end(&_lines[_selectionStartY].c_str()[endPos]);
+
+ _undobuffer = _engine->_inputText;
+ _engine->_inputText = beg + end;
+ _clipboard = mid;
+ drawInput();
+
+ _menu->enableCommand(kMenuEdit, kMenuActionUndo, true);
+ _menu->enableCommand(kMenuEdit, kMenuActionPaste, true);
+
+ _selectionStartY = -1;
+ _selectionEndY = -1;
+}
+
+void Gui::disableUndo() {
+ _menu->enableCommand(kMenuEdit, kMenuActionUndo, false);
+}
+
+void Gui::disableAllMenus() {
+ _menu->disableAllMenus();
+}
+
+void Gui::enableNewGameMenus() {
+ _menu->enableCommand(kMenuFile, kMenuActionNew, true);
+ _menu->enableCommand(kMenuFile, kMenuActionOpen, true);
+ _menu->enableCommand(kMenuFile, kMenuActionQuit, true);
+}
+
+} // End of namespace Wage
diff --git a/engines/wage/gui.cpp b/engines/wage/gui.cpp
new file mode 100644
index 0000000000..9dd1a24b3c
--- /dev/null
+++ b/engines/wage/gui.cpp
@@ -0,0 +1,675 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * MIT License:
+ *
+ * Copyright (c) 2009 Alexei Svitkine, Eugene Sandulenko
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include "common/timer.h"
+#include "common/unzip.h"
+#include "graphics/cursorman.h"
+#include "graphics/fonts/bdf.h"
+#include "graphics/palette.h"
+
+#include "wage/wage.h"
+#include "wage/design.h"
+#include "wage/entities.h"
+#include "wage/menu.h"
+#include "wage/gui.h"
+#include "wage/world.h"
+
+namespace Wage {
+
+static const byte palette[] = {
+ 0, 0, 0, // Black
+ 0x80, 0x80, 0x80, // Gray
+ 0xff, 0xff, 0xff, // White
+ 0x00, 0xff, 0x00 // Green
+};
+
+static byte fillPatterns[][8] = { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, // kPatternSolid
+ { 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55 }, // kPatternStripes
+ { 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55 }, // kPatternCheckers
+ { 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa } // kPatternCheckers2
+};
+
+static const byte macCursorArrow[] = {
+ 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 2, 0, 2, 3, 3, 3, 3, 3, 3, 3, 3,
+ 2, 0, 0, 2, 3, 3, 3, 3, 3, 3, 3,
+ 2, 0, 0, 0, 2, 3, 3, 3, 3, 3, 3,
+ 2, 0, 0, 0, 0, 2, 3, 3, 3, 3, 3,
+ 2, 0, 0, 0, 0, 0, 2, 3, 3, 3, 3,
+ 2, 0, 0, 0, 0, 0, 0, 2, 3, 3, 3,
+ 2, 0, 0, 0, 0, 0, 0, 0, 2, 3, 3,
+ 2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3,
+ 2, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2,
+ 2, 0, 0, 2, 0, 0, 2, 3, 3, 3, 3,
+ 2, 0, 2, 3, 2, 0, 0, 2, 3, 3, 3,
+ 2, 2, 3, 3, 2, 0, 0, 2, 3, 3, 3,
+ 2, 3, 3, 3, 3, 2, 0, 0, 2, 3, 3,
+ 3, 3, 3, 3, 3, 2, 0, 0, 2, 3, 3,
+ 3, 3, 3, 3, 3, 3, 2, 2, 2, 3, 3
+};
+
+static const byte macCursorBeam[] = {
+ 0, 0, 3, 3, 3, 0, 0, 3, 3, 3, 3,
+ 3, 3, 0, 3, 0, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 0, 3, 0, 3, 3, 3, 3, 3, 3,
+ 0, 0, 3, 3, 3, 0, 0, 3, 3, 3, 3,
+};
+
+static void cursorTimerHandler(void *refCon) {
+ Gui *gui = (Gui *)refCon;
+
+ int x = gui->_cursorX;
+ int y = gui->_cursorY;
+
+ if (x == 0 && y == 0)
+ return;
+
+ if (!gui->_screen.getPixels())
+ return;
+
+ x += gui->_consoleTextArea.left;
+ y += gui->_consoleTextArea.top;
+
+ gui->_screen.vLine(x, y, y + kCursorHeight, gui->_cursorState ? kColorBlack : kColorWhite);
+
+ if (!gui->_cursorOff)
+ gui->_cursorState = !gui->_cursorState;
+
+ gui->_cursorRect.left = x;
+ gui->_cursorRect.right = MIN<uint16>(x + 1, gui->_screen.w);
+ gui->_cursorRect.top = y;
+ gui->_cursorRect.bottom = MIN<uint16>(y + kCursorHeight, gui->_screen.h);
+
+ gui->_cursorDirty = true;
+}
+
+Gui::Gui(WageEngine *engine) {
+ _engine = engine;
+ _scene = NULL;
+ _sceneDirty = true;
+ _consoleDirty = true;
+ _bordersDirty = true;
+ _menuDirty = true;
+ _cursorDirty = false;
+ _consoleFullRedraw = true;
+ _screen.create(g_system->getWidth(), g_system->getHeight(), Graphics::PixelFormat::createFormatCLUT8());
+
+ _scrollPos = 0;
+ _consoleLineHeight = 8; // Dummy value which makes sense
+ _consoleNumLines = 24; // Dummy value
+ _builtInFonts = false;
+ _sceneIsActive = false;
+
+ _cursorX = 0;
+ _cursorY = 0;
+ _cursorState = false;
+ _cursorOff = false;
+
+ _inTextSelection = false;
+ _selectionStartX = _selectionStartY = -1;
+ _selectionEndX = _selectionEndY = -1;
+
+ _inputTextLineNum = 0;
+
+ g_system->getPaletteManager()->setPalette(palette, 0, 4);
+
+ CursorMan.replaceCursorPalette(palette, 0, 4);
+ CursorMan.replaceCursor(macCursorArrow, 11, 16, 1, 1, 3);
+ _cursorIsArrow = true;
+ CursorMan.showMouse(true);
+
+ for (int i = 0; i < ARRAYSIZE(fillPatterns); i++)
+ _patterns.push_back(fillPatterns[i]);
+
+ loadFonts();
+
+ g_system->getTimerManager()->installTimerProc(&cursorTimerHandler, 200000, this, "wageCursor");
+
+ _menu = new Menu(this);
+}
+
+Gui::~Gui() {
+ _screen.free();
+ _console.free();
+ g_system->getTimerManager()->removeTimerProc(&cursorTimerHandler);
+ delete _menu;
+}
+
+void Gui::undrawCursor() {
+ _cursorOff = true;
+ _cursorState = false;
+ cursorTimerHandler(this);
+ _cursorOff = false;
+}
+
+const Graphics::Font *Gui::getFont(const char *name, Graphics::FontManager::FontUsage fallback) {
+ const Graphics::Font *font = 0;
+
+ if (!_builtInFonts) {
+ font = FontMan.getFontByName(name);
+
+ if (!font)
+ warning("Cannot load font %s", name);
+ }
+
+ if (_builtInFonts || !font)
+ font = FontMan.getFontByUsage(fallback);
+
+ return font;
+}
+
+const Graphics::Font *Gui::getTitleFont() {
+ return getFont("Chicago-12", Graphics::FontManager::kBigGUIFont);
+}
+
+void Gui::drawDesktop() {
+ // Draw desktop
+ Common::Rect r(0, 0, _screen.w - 1, _screen.h - 1);
+ Design::drawFilledRoundRect(&_screen, r, kDesktopArc, kColorBlack, _patterns, kPatternCheckers);
+ g_system->copyRectToScreen(_screen.getPixels(), _screen.pitch, 0, 0, _screen.w, _screen.h);
+}
+
+void Gui::draw() {
+ if (_engine->_isGameOver) {
+ if (_menuDirty) {
+ drawDesktop();
+ _menu->render();
+ }
+
+ _menuDirty = false;
+
+ return;
+ }
+
+ if (_scene != _engine->_world->_player->_currentScene || _sceneDirty) {
+ _scene = _engine->_world->_player->_currentScene;
+
+ drawDesktop();
+
+ _sceneDirty = true;
+ _consoleDirty = true;
+ _menuDirty = true;
+ _consoleFullRedraw = true;
+
+ _scene->paint(&_screen, _scene->_designBounds->left, _scene->_designBounds->top);
+
+ _sceneArea.left = _scene->_designBounds->left + kBorderWidth - 2;
+ _sceneArea.top = _scene->_designBounds->top + kBorderWidth - 2;
+ _sceneArea.setWidth(_scene->_designBounds->width() - 2 * kBorderWidth);
+ _sceneArea.setHeight(_scene->_designBounds->height() - 2 * kBorderWidth);
+
+ _consoleTextArea.left = _scene->_textBounds->left + kBorderWidth - 2;
+ _consoleTextArea.top = _scene->_textBounds->top + kBorderWidth - 2;
+ _consoleTextArea.setWidth(_scene->_textBounds->width() - 2 * kBorderWidth);
+ _consoleTextArea.setHeight(_scene->_textBounds->height() - 2 * kBorderWidth);
+ }
+
+ if (_scene && (_bordersDirty || _sceneDirty))
+ paintBorder(&_screen, _sceneArea, kWindowScene);
+
+ // Render console
+ if (_consoleDirty || _consoleFullRedraw)
+ renderConsole(&_screen, _consoleTextArea);
+
+ if (_bordersDirty || _consoleDirty || _consoleFullRedraw)
+ paintBorder(&_screen, _consoleTextArea, kWindowConsole);
+
+ if (_menuDirty)
+ _menu->render();
+
+ if (_cursorDirty) {
+ g_system->copyRectToScreen(_screen.getBasePtr(_cursorRect.left, _cursorRect.top), _screen.pitch,
+ _cursorRect.left, _cursorRect.top, _cursorRect.width(), _cursorRect.height());
+
+ _cursorDirty = false;
+ }
+
+ _sceneDirty = false;
+ _consoleDirty = false;
+ _bordersDirty = false;
+ _menuDirty = false;
+ _consoleFullRedraw = false;
+}
+
+void Gui::drawBox(Graphics::Surface *g, int x, int y, int w, int h) {
+ Common::Rect r(x, y, x + w + 1, y + h + 1);
+
+ g->fillRect(r, kColorWhite);
+ g->frameRect(r, kColorBlack);
+}
+
+void Gui::fillRect(Graphics::Surface *g, int x, int y, int w, int h, int color) {
+ Common::Rect r(x, y, x + w, y + h);
+
+ g->fillRect(r, color);
+}
+
+#define ARROW_W 12
+#define ARROW_H 6
+const int arrowPixels[ARROW_H][ARROW_W] = {
+ {0,0,0,0,0,1,1,0,0,0,0,0},
+ {0,0,0,0,1,1,1,1,0,0,0,0},
+ {0,0,0,1,1,1,1,1,1,0,0,0},
+ {0,0,1,1,1,1,1,1,1,1,0,0},
+ {0,1,1,1,1,1,1,1,1,1,1,0},
+ {1,1,1,1,1,1,1,1,1,1,1,1}};
+
+void Gui::paintBorder(Graphics::Surface *g, Common::Rect &r, WindowType windowType, int highlightedPart) {
+ bool active = false, scrollable = false, closeable = false, drawTitle = false;
+ const int size = kBorderWidth;
+ int x = r.left - size;
+ int y = r.top - size;
+ int width = r.width() + 2 * size;
+ int height = r.height() + 2 * size;
+
+ switch (windowType) {
+ case kWindowScene:
+ active = _sceneIsActive;
+ scrollable = false;
+ closeable = _sceneIsActive;
+ drawTitle = true;
+ break;
+ case kWindowConsole:
+ active = !_sceneIsActive;
+ scrollable = true;
+ closeable = !_sceneIsActive;
+ drawTitle = false;
+ break;
+ }
+
+ drawBox(g, x, y, size, size);
+ drawBox(g, x + width - size - 1, y, size, size);
+ drawBox(g, x + width - size - 1, y + height - size - 1, size, size);
+ drawBox(g, x, y + height - size - 1, size, size);
+ drawBox(g, x + size, y + 2, width - 2 * size - 1, size - 4);
+ drawBox(g, x + size, y + height - size + 1, width - 2 * size - 1, size - 4);
+ drawBox(g, x + 2, y + size, size - 4, height - 2 * size - 1);
+ drawBox(g, x + width - size + 1, y + size, size - 4, height - 2 * size - 1);
+
+ if (active) {
+ fillRect(g, x + size, y + 5, width - 2 * size - 1, 8);
+ fillRect(g, x + size, y + height - 13, width - 2 * size - 1, 8);
+ fillRect(g, x + 5, y + size, 8, height - 2 * size - 1);
+ if (!scrollable) {
+ fillRect(g, x + width - 13, y + size, 8, height - 2 * size - 1);
+ } else {
+ int x1 = x + width - 15;
+ int y1 = y + size + 1;
+ int color1 = kColorBlack;
+ int color2 = kColorWhite;
+ if (highlightedPart == kBorderScrollUp) {
+ SWAP(color1, color2);
+ fillRect(g, x + width - kBorderWidth + 2, y + size, size - 4, r.height() / 2);
+ }
+ for (int yy = 0; yy < ARROW_H; yy++) {
+ for (int xx = 0; xx < ARROW_W; xx++) {
+ if (arrowPixels[yy][xx] != 0) {
+ g->hLine(x1 + xx, y1 + yy, x1 + xx, color1);
+ } else {
+ g->hLine(x1 + xx, y1 + yy, x1 + xx, color2);
+ }
+ }
+ }
+ fillRect(g, x + width - 13, y + size + ARROW_H, 8, r.height() / 2 - ARROW_H, color1);
+
+ color1 = kColorBlack;
+ color2 = kColorWhite;
+ if (highlightedPart == kBorderScrollDown) {
+ SWAP(color1, color2);
+ fillRect(g, x + width - kBorderWidth + 2, y + size + r.height() / 2, size - 4, r.height() / 2);
+ }
+ fillRect(g, x + width - 13, y + size + r.height() / 2, 8, r.height() / 2 - ARROW_H, color1);
+ y1 += height - 2 * size - ARROW_H - 2;
+ for (int yy = 0; yy < ARROW_H; yy++) {
+ for (int xx = 0; xx < ARROW_W; xx++) {
+ if (arrowPixels[ARROW_H - yy - 1][xx] != 0) {
+ g->hLine(x1 + xx, y1 + yy, x1 + xx, color1);
+ } else {
+ g->hLine(x1 + xx, y1 + yy, x1 + xx, color2);
+ }
+ }
+ }
+ }
+ if (closeable) {
+ if (highlightedPart == kBorderCloseButton) {
+ fillRect(g, x + 6, y + 6, 6, 6);
+ } else {
+ drawBox(g, x + 5, y + 5, 7, 7);
+ }
+ }
+ }
+
+ if (drawTitle) {
+ const Graphics::Font *font = getTitleFont();
+ int yOff = _builtInFonts ? 3 : 1;
+
+ int w = font->getStringWidth(_scene->_name) + 10;
+ int maxWidth = width - size * 2 - 7;
+ if (w > maxWidth)
+ w = maxWidth;
+ drawBox(g, x + (width - w) / 2, y, w, size);
+ font->drawString(g, _scene->_name, x + (width - w) / 2 + 5, y + yOff, w, kColorBlack);
+ }
+
+ if (x < 0) {
+ width += x;
+ x = 0;
+ }
+ if (y < 0) {
+ height += y;
+ y = 0;
+ }
+ if (x + width > _screen.w)
+ width = _screen.w - x;
+ if (y + height > _screen.h)
+ height = _screen.h - y;
+
+ g_system->copyRectToScreen(g->getBasePtr(x, y), g->pitch, x, y, width, height);
+}
+
+void Gui::loadFonts() {
+ Common::Archive *dat;
+
+ dat = Common::makeZipArchive("wage.dat");
+
+ if (!dat) {
+ warning("Could not find wage.dat. Falling back to built-in fonts");
+ _builtInFonts = true;
+
+ return;
+ }
+
+ Common::ArchiveMemberList list;
+ dat->listMembers(list);
+
+ for (Common::ArchiveMemberList::iterator it = list.begin(); it != list.end(); ++it) {
+ Common::SeekableReadStream *stream = dat->createReadStreamForMember((*it)->getName());
+
+ Graphics::BdfFont *font = Graphics::BdfFont::loadFont(*stream);
+
+ delete stream;
+
+ Common::String fontName = (*it)->getName();
+
+ // Trim the .bdf extension
+ for (int i = fontName.size() - 1; i >= 0; --i) {
+ if (fontName[i] == '.') {
+ while ((uint)i < fontName.size()) {
+ fontName.deleteLastChar();
+ }
+ break;
+ }
+ }
+
+ FontMan.assignFontToName(fontName, font);
+
+ debug(2, " %s", fontName.c_str());
+ }
+
+ _builtInFonts = false;
+
+ delete dat;
+}
+
+void Gui::regenCommandsMenu() {
+ _menu->regenCommandsMenu();
+}
+
+void Gui::regenWeaponsMenu() {
+ _menu->regenWeaponsMenu();
+}
+
+void Gui::processMenuShortCut(byte flags, uint16 ascii) {
+ _menu->processMenuShortCut(flags, ascii);
+}
+
+void Gui::mouseMove(int x, int y) {
+ if (_menu->_menuActivated) {
+ if (_menu->mouseMove(x, y))
+ _menuDirty = true;
+
+ return;
+ }
+
+ if (_inTextSelection) {
+ updateTextSelection(x, y);
+ return;
+ }
+
+ if (_consoleTextArea.contains(x, y)) {
+ if (_cursorIsArrow) {
+ CursorMan.replaceCursor(macCursorBeam, 11, 16, 3, 8, 3);
+ _cursorIsArrow = false;
+ }
+ } else if (_cursorIsArrow == false) {
+ CursorMan.replaceCursor(macCursorArrow, 11, 16, 1, 1, 3);
+ _cursorIsArrow = true;
+ }
+}
+
+void Gui::pushArrowCursor() {
+ CursorMan.pushCursor(macCursorArrow, 11, 16, 1, 1, 3);
+}
+
+void Gui::popCursor() {
+ CursorMan.popCursor();
+}
+
+static int isInBorder(Common::Rect &rect, int x, int y) {
+ if (x >= rect.left - kBorderWidth && x < rect.left && y >= rect.top - kBorderWidth && y < rect.top)
+ return kBorderCloseButton;
+
+ if (x >= rect.right && x < rect.right + kBorderWidth) {
+ if (y < rect.top - kBorderWidth)
+ return kBorderNone;
+
+ if (y >= rect.bottom + kBorderWidth)
+ return kBorderNone;
+
+ if (y >= rect.top + rect.height() / 2)
+ return kBorderScrollDown;
+
+ return kBorderScrollUp;
+ }
+
+ return kBorderNone;
+}
+
+Designed *Gui::mouseUp(int x, int y) {
+ if (_menu->_menuActivated) {
+ if (_menu->mouseRelease(x, y)) {
+ _sceneDirty = true;
+ _consoleDirty = true;
+ _bordersDirty = true;
+ _menuDirty = true;
+ }
+
+ return NULL;
+ }
+
+ if (_inTextSelection) {
+ _inTextSelection = false;
+
+ if (_selectionEndY == -1 ||
+ (_selectionEndX == _selectionStartX && _selectionEndY == _selectionStartY)) {
+ _selectionStartY = _selectionEndY = -1;
+ _consoleFullRedraw = true;
+ _menu->enableCommand(kMenuEdit, kMenuActionCopy, false);
+ } else {
+ _menu->enableCommand(kMenuEdit, kMenuActionCopy, true);
+
+ bool cutAllowed = false;
+
+ if (_selectionStartY == _selectionEndY && _selectionStartY == (int)_lines.size() - 1)
+ cutAllowed = true;
+
+ _menu->enableCommand(kMenuEdit, kMenuActionCut, cutAllowed);
+ _menu->enableCommand(kMenuEdit, kMenuActionClear, cutAllowed);
+ }
+ }
+
+ int borderClick;
+
+ if (_sceneArea.contains(x, y)) {
+ if (!_sceneIsActive) {
+ _sceneIsActive = true;
+ _bordersDirty = true;
+ }
+
+ for (ObjList::const_iterator it = _scene->_objs.begin(); it != _scene->_objs.end(); ++it) {
+ if ((*it)->_design->isPointOpaque(x - _sceneArea.left + kBorderWidth, y - _sceneArea.top + kBorderWidth))
+ return *it;
+ }
+
+ for (ChrList::const_iterator it = _scene->_chrs.begin(); it != _scene->_chrs.end(); ++it) {
+ if ((*it)->_design->isPointOpaque(x - _sceneArea.left + kBorderWidth, y - _sceneArea.top + kBorderWidth))
+ return *it;
+ }
+ } else if (_consoleTextArea.contains(x, y)) {
+ if (_sceneIsActive) {
+ _sceneIsActive = false;
+ _bordersDirty = true;
+ }
+ } else if ((borderClick = isInBorder(_consoleTextArea, x, y)) != kBorderNone) {
+ _bordersDirty = true;
+ int _oldScrollPos = _scrollPos;
+
+ switch (borderClick) {
+ case kBorderScrollUp:
+ _scrollPos = MAX<int>(0, _scrollPos - _consoleLineHeight);
+ undrawCursor();
+ _cursorY -= (_scrollPos - _oldScrollPos);
+ _consoleDirty = true;
+ _consoleFullRedraw = true;
+ break;
+ case kBorderScrollDown:
+ _scrollPos = MIN<int>((_lines.size() - 2) * _consoleLineHeight, _scrollPos + _consoleLineHeight);
+ undrawCursor();
+ _cursorY -= (_scrollPos - _oldScrollPos);
+ _consoleDirty = true;
+ _consoleFullRedraw = true;
+ break;
+ }
+ }
+
+ return NULL;
+}
+
+void Gui::mouseDown(int x, int y) {
+ int borderClick;
+
+ if (_menu->mouseClick(x, y)) {
+ _menuDirty = true;
+ } else if (_consoleTextArea.contains(x, y)) {
+ startMarking(x, y);
+ } else if ((borderClick = isInBorder(_consoleTextArea, x, y)) != kBorderNone) {
+ paintBorder(&_screen, _consoleTextArea, kWindowConsole, borderClick);
+ }
+}
+
+int Gui::calcTextX(int x, int textLine) {
+ const Graphics::Font *font = getConsoleFont();
+
+ if ((uint)textLine >= _lines.size())
+ return 0;
+
+ Common::String str = _lines[textLine];
+
+ x -= _consoleTextArea.left;
+
+ for (int i = str.size(); i >= 0; i--) {
+ if (font->getStringWidth(str) < x) {
+ return i;
+ }
+
+ str.deleteLastChar();
+ }
+
+ return 0;
+}
+
+int Gui::calcTextY(int y) {
+ y -= _consoleTextArea.top;
+
+ if (y < 0)
+ y = 0;
+
+ const int firstLine = _scrollPos / _consoleLineHeight;
+ int textLine = (y - _scrollPos % _consoleLineHeight) / _consoleLineHeight + firstLine;
+
+ return textLine;
+}
+
+void Gui::startMarking(int x, int y) {
+ _selectionStartY = calcTextY(y);
+ _selectionStartX = calcTextX(x, _selectionStartY);
+
+ _selectionEndY = -1;
+
+ _inTextSelection = true;
+}
+
+void Gui::updateTextSelection(int x, int y) {
+ _selectionEndY = calcTextY(y);
+ _selectionEndX = calcTextX(x, _selectionEndY);
+
+ _consoleFullRedraw = true;
+}
+
+} // End of namespace Wage
diff --git a/engines/wage/gui.h b/engines/wage/gui.h
new file mode 100644
index 0000000000..73814d39b4
--- /dev/null
+++ b/engines/wage/gui.h
@@ -0,0 +1,189 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * MIT License:
+ *
+ * Copyright (c) 2009 Alexei Svitkine, Eugene Sandulenko
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#ifndef WAGE_GUI_H
+#define WAGE_GUI_H
+
+#include "common/str-array.h"
+#include "graphics/font.h"
+#include "graphics/fontman.h"
+#include "graphics/surface.h"
+#include "common/rect.h"
+
+namespace Wage {
+
+class Menu;
+
+enum WindowType {
+ kWindowScene,
+ kWindowConsole
+};
+
+enum {
+ kMenuHeight = 20,
+ kMenuLeftMargin = 7,
+ kMenuSpacing = 13,
+ kMenuPadding = 16,
+ kMenuDropdownPadding = 14,
+ kMenuDropdownItemHeight = 16,
+ kMenuItemHeight = 20,
+ kBorderWidth = 17,
+ kDesktopArc = 7,
+ kComponentsPadding = 10,
+ kCursorHeight = 12
+};
+
+enum {
+ kPatternSolid = 1,
+ kPatternStripes = 2,
+ kPatternCheckers = 3,
+ kPatternCheckers2 = 4
+};
+
+enum {
+ kBorderNone = 0,
+ kBorderScrollUp,
+ kBorderScrollDown,
+ kBorderCloseButton
+};
+
+class Gui {
+public:
+ Gui(WageEngine *engine);
+ ~Gui();
+
+ void draw();
+ void appendText(const char *str);
+ void clearOutput();
+ void mouseMove(int x, int y);
+ void mouseDown(int x, int y);
+ Designed *mouseUp(int x, int y);
+ void drawInput();
+ void setSceneDirty() { _sceneDirty = true; }
+ const Graphics::Font *getFont(const char *name, Graphics::FontManager::FontUsage fallback);
+ void regenCommandsMenu();
+ void regenWeaponsMenu();
+ void processMenuShortCut(byte flags, uint16 ascii);
+ void pushArrowCursor();
+ void popCursor();
+
+ void actionCopy();
+ void actionPaste();
+ void actionUndo();
+ void actionClear();
+ void actionCut();
+ void disableUndo();
+ void disableAllMenus();
+ void enableNewGameMenus();
+
+private:
+ void undrawCursor();
+ void drawDesktop();
+ void paintBorder(Graphics::Surface *g, Common::Rect &r, WindowType windowType, int highlightedPart = kBorderNone);
+ void renderConsole(Graphics::Surface *g, Common::Rect &r);
+ void drawBox(Graphics::Surface *g, int x, int y, int w, int h);
+ void fillRect(Graphics::Surface *g, int x, int y, int w, int h, int color = kColorBlack);
+ void loadFonts();
+ void flowText(Common::String &str);
+ const Graphics::Font *getConsoleFont();
+ const Graphics::Font *getTitleFont();
+ void startMarking(int x, int y);
+ int calcTextX(int x, int textLine);
+ int calcTextY(int y);
+ void updateTextSelection(int x, int y);
+
+public:
+ Graphics::Surface _screen;
+ int _cursorX, _cursorY;
+ bool _cursorState;
+ Common::Rect _consoleTextArea;
+
+ bool _builtInFonts;
+ WageEngine *_engine;
+
+ Patterns _patterns;
+
+ bool _cursorDirty;
+ Common::Rect _cursorRect;
+ bool _cursorOff;
+
+ bool _menuDirty;
+
+private:
+ Graphics::Surface _console;
+ Menu *_menu;
+ Scene *_scene;
+ bool _sceneDirty;
+ bool _consoleDirty;
+ bool _bordersDirty;
+
+ Common::StringArray _out;
+ Common::StringArray _lines;
+ uint _scrollPos;
+ int _consoleLineHeight;
+ uint _consoleNumLines;
+ bool _consoleFullRedraw;
+
+ Common::Rect _sceneArea;
+ bool _sceneIsActive;
+ bool _cursorIsArrow;
+
+ bool _inTextSelection;
+ int _selectionStartX;
+ int _selectionStartY;
+ int _selectionEndX;
+ int _selectionEndY;
+
+ Common::String _clipboard;
+ Common::String _undobuffer;
+
+ int _inputTextLineNum;
+};
+
+} // End of namespace Wage
+
+#endif
diff --git a/engines/wage/menu.cpp b/engines/wage/menu.cpp
new file mode 100644
index 0000000000..12ef8c2219
--- /dev/null
+++ b/engines/wage/menu.cpp
@@ -0,0 +1,571 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * MIT License:
+ *
+ * Copyright (c) 2009 Alexei Svitkine, Eugene Sandulenko
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include "common/system.h"
+#include "common/keyboard.h"
+
+#include "wage/wage.h"
+#include "wage/entities.h"
+#include "wage/design.h"
+#include "wage/gui.h"
+#include "wage/menu.h"
+#include "wage/world.h"
+
+namespace Wage {
+
+struct MenuSubItem {
+ Common::String text;
+ int action;
+ int style;
+ char shortcut;
+ bool enabled;
+ Common::Rect bbox;
+
+ MenuSubItem(const char *t, int a, int s = 0, char sh = 0, bool e = true) : text(t), action(a), style(s), shortcut(sh), enabled(e) {}
+};
+
+typedef Common::Array<MenuSubItem *> SubItemArray;
+
+struct MenuItem {
+ Common::String name;
+ SubItemArray subitems;
+ Common::Rect bbox;
+ Common::Rect subbbox;
+
+ MenuItem(const char *n) : name(n) {}
+};
+
+struct MenuData {
+ int menunum;
+ const char *title;
+ int action;
+ byte shortcut;
+ bool enabled;
+} static const menuSubItems[] = {
+ { kMenuFile, "New", kMenuActionNew, 0, false },
+ { kMenuFile, "Open...", kMenuActionOpen, 0, false },
+ { kMenuFile, "Close", kMenuActionClose, 0, true },
+ { kMenuFile, "Save", kMenuActionSave, 0, false },
+ { kMenuFile, "Save as...", kMenuActionSaveAs, 0, true },
+ { kMenuFile, "Revert", kMenuActionRevert, 0, false },
+ { kMenuFile, "Quit", kMenuActionQuit, 0, true },
+
+ { kMenuEdit, "Undo", kMenuActionUndo, 'Z', false },
+ { kMenuEdit, NULL, 0, 0, false },
+ { kMenuEdit, "Cut", kMenuActionCut, 'K', false },
+ { kMenuEdit, "Copy", kMenuActionCopy, 'C', false },
+ { kMenuEdit, "Paste", kMenuActionPaste, 'V', false },
+ { kMenuEdit, "Clear", kMenuActionClear, 'B', false },
+
+ { 0, NULL, 0, 0, false }
+};
+
+Menu::Menu(Gui *gui) : _gui(gui) {
+ assert(_gui->_engine);
+ assert(_gui->_engine->_world);
+
+ _font = getMenuFont();
+
+ MenuItem *about = new MenuItem(_gui->_builtInFonts ? "\xa9" : "\xf0"); // (c) Symbol as the most resembling apple
+ _items.push_back(about);
+ _items[0]->subitems.push_back(new MenuSubItem(_gui->_engine->_world->getAboutMenuItemName(), kMenuActionAbout));
+
+ MenuItem *file = new MenuItem("File");
+ _items.push_back(file);
+
+ MenuItem *edit = new MenuItem("Edit");
+ _items.push_back(edit);
+
+ for (int i = 0; menuSubItems[i].menunum; i++) {
+ const MenuData *m = &menuSubItems[i];
+
+ _items[m->menunum]->subitems.push_back(new MenuSubItem(m->title, m->action, 0, m->shortcut, m->enabled));
+ }
+
+ _commands = new MenuItem(_gui->_engine->_world->_commandsMenuName.c_str());
+ _items.push_back(_commands);
+ regenCommandsMenu();
+
+ _weapons = NULL;
+
+ if (!_gui->_engine->_world->_weaponMenuDisabled) {
+ _weapons = new MenuItem(_gui->_engine->_world->_weaponsMenuName.c_str());
+ _items.push_back(_weapons);
+
+ regenWeaponsMenu();
+ }
+
+ // Calculate menu dimensions
+ int y = 1;
+ int x = 18;
+
+ for (uint i = 0; i < _items.size(); i++) {
+ int w = _font->getStringWidth(_items[i]->name);
+
+ if (_items[i]->bbox.bottom == 0) {
+ _items[i]->bbox.left = x - kMenuLeftMargin;
+ _items[i]->bbox.top = y;
+ _items[i]->bbox.right = x + w + kMenuSpacing - kMenuLeftMargin;
+ _items[i]->bbox.bottom = y + _font->getFontHeight() + (_gui->_builtInFonts ? 3 : 2);
+ }
+
+ calcMenuBounds(_items[i]);
+
+ x += w + kMenuSpacing;
+ }
+
+ _bbox.left = 0;
+ _bbox.top = 0;
+ _bbox.right = _gui->_screen.w - 1;
+ _bbox.bottom = kMenuHeight - 1;
+
+ _menuActivated = false;
+ _activeItem = -1;
+ _activeSubItem = -1;
+
+ _screenCopy.create(_gui->_screen.w, _gui->_screen.h, Graphics::PixelFormat::createFormatCLUT8());
+ _tempSurface.create(_gui->_screen.w, _font->getFontHeight(), Graphics::PixelFormat::createFormatCLUT8());
+}
+
+Menu::~Menu() {
+ for (uint i = 0; i < _items.size(); i++) {
+ for (uint j = 0; j < _items[i]->subitems.size(); j++)
+ delete _items[i]->subitems[j];
+ delete _items[i];
+ }
+}
+
+void Menu::regenCommandsMenu() {
+ for (uint j = 0; j < _commands->subitems.size(); j++)
+ delete _commands->subitems[j];
+
+ _commands->subitems.clear();
+
+ createCommandsMenu(_commands);
+ calcMenuBounds(_commands);
+}
+
+void Menu::createCommandsMenu(MenuItem *menu) {
+ Common::String string(_gui->_engine->_world->_commandsMenu);
+
+ Common::String item;
+
+ for (uint i = 0; i < string.size(); i++) {
+ while(i < string.size() && string[i] != ';') // Read token
+ item += string[i++];
+
+ if (item == "(-") {
+ menu->subitems.push_back(new MenuSubItem(NULL, 0));
+ } else {
+ bool enabled = true;
+ int style = 0;
+ char shortcut = 0;
+ const char *shortPtr = strrchr(item.c_str(), '/');
+ if (shortPtr != NULL) {
+ if (strlen(shortPtr) >= 2) {
+ shortcut = shortPtr[1];
+ item.deleteChar(shortPtr - item.c_str());
+ item.deleteChar(shortPtr - item.c_str());
+ } else {
+ error("Unexpected shortcut: '%s', item '%s' in menu '%s'", shortPtr, item.c_str(), string.c_str());
+ }
+ }
+
+ while (item.size() >= 2 && item[item.size() - 2] == '<') {
+ char c = item.lastChar();
+ if (c == 'B') {
+ style |= kFontStyleBold;
+ } else if (c == 'I') {
+ style |= kFontStyleItalic;
+ } else if (c == 'U') {
+ style |= kFontStyleUnderline;
+ } else if (c == 'O') {
+ style |= kFontStyleOutline;
+ } else if (c == 'S') {
+ style |= kFontStyleShadow;
+ } else if (c == 'C') {
+ style |= kFontStyleCondensed;
+ } else if (c == 'E') {
+ style |= kFontStyleExtended;
+ }
+ item.deleteLastChar();
+ item.deleteLastChar();
+ }
+
+ Common::String tmpitem(item);
+ tmpitem.trim();
+ if (tmpitem[0] == '(') {
+ enabled = false;
+
+ for (uint j = 0; j < item.size(); j++)
+ if (item[j] == '(') {
+ item.deleteChar(j);
+ break;
+ }
+ }
+
+ menu->subitems.push_back(new MenuSubItem(item.c_str(), kMenuActionCommand, style, shortcut, enabled));
+ }
+
+ item.clear();
+ }
+}
+
+void Menu::regenWeaponsMenu() {
+ if (_gui->_engine->_world->_weaponMenuDisabled)
+ return;
+
+ for (uint j = 0; j < _weapons->subitems.size(); j++)
+ delete _weapons->subitems[j];
+
+ _weapons->subitems.clear();
+
+ createWeaponsMenu(_weapons);
+ calcMenuBounds(_weapons);
+}
+
+void Menu::createWeaponsMenu(MenuItem *menu) {
+ Chr *player = _gui->_engine->_world->_player;
+ ObjArray *weapons = player->getWeapons(true);
+
+ for (uint i = 0; i < weapons->size(); i++) {
+ Obj *obj = (*weapons)[i];
+ if (obj->_type == Obj::REGULAR_WEAPON ||
+ obj->_type == Obj::THROW_WEAPON ||
+ obj->_type == Obj::MAGICAL_OBJECT) {
+ Common::String command(obj->_operativeVerb);
+ command += " ";
+ command += obj->_name;
+
+ menu->subitems.push_back(new MenuSubItem(command.c_str(), kMenuActionCommand, 0, 0, true));
+ }
+ }
+ delete weapons;
+
+ if (menu->subitems.empty())
+ menu->subitems.push_back(new MenuSubItem("You have no weapons", 0, 0, 0, false));
+}
+
+const Graphics::Font *Menu::getMenuFont() {
+ return _gui->getFont("Chicago-12", Graphics::FontManager::kBigGUIFont);
+}
+
+const char *Menu::getAcceleratorString(MenuSubItem *item, const char *prefix) {
+ static char res[20];
+ *res = 0;
+
+ if (item->shortcut != 0)
+ sprintf(res, "%s%c%c", prefix, (_gui->_builtInFonts ? '^' : '\x11'), item->shortcut);
+
+ return res;
+}
+
+int Menu::calculateMenuWidth(MenuItem *menu) {
+ int maxWidth = 0;
+ for (uint i = 0; i < menu->subitems.size(); i++) {
+ MenuSubItem *item = menu->subitems[i];
+ if (!item->text.empty()) {
+ Common::String text(item->text);
+ Common::String acceleratorText(getAcceleratorString(item, " "));
+ if (!acceleratorText.empty()) {
+ text += acceleratorText;
+ }
+
+ int width = _font->getStringWidth(text);
+ if (width > maxWidth) {
+ maxWidth = width;
+ }
+ }
+ }
+ return maxWidth;
+}
+
+void Menu::calcMenuBounds(MenuItem *menu) {
+ // TODO: cache maxWidth
+ int maxWidth = calculateMenuWidth(menu);
+ int x1 = menu->bbox.left - 1;
+ int y1 = menu->bbox.bottom + 1;
+ int x2 = x1 + maxWidth + kMenuDropdownPadding * 2 - 4;
+ int y2 = y1 + menu->subitems.size() * kMenuDropdownItemHeight + 2;
+
+ menu->subbbox.left = x1;
+ menu->subbbox.top = y1;
+ menu->subbbox.right = x2;
+ menu->subbbox.bottom = y2;
+}
+
+void Menu::render() {
+ Common::Rect r(_bbox);
+
+ Design::drawFilledRoundRect(&_gui->_screen, r, kDesktopArc, kColorWhite, _gui->_patterns, kPatternSolid);
+ r.top = 7;
+ Design::drawFilledRect(&_gui->_screen, r, kColorWhite, _gui->_patterns, kPatternSolid);
+ r.top = kMenuHeight - 1;
+ Design::drawFilledRect(&_gui->_screen, r, kColorBlack, _gui->_patterns, kPatternSolid);
+
+ for (uint i = 0; i < _items.size(); i++) {
+ int color = kColorBlack;
+ MenuItem *it = _items[i];
+
+ if ((uint)_activeItem == i) {
+ Common::Rect hbox = it->bbox;
+
+ hbox.left -= 1;
+ hbox.right += 2;
+
+ Design::drawFilledRect(&_gui->_screen, hbox, kColorBlack, _gui->_patterns, kPatternSolid);
+ color = kColorWhite;
+
+ if (!it->subitems.empty())
+ renderSubmenu(it);
+ }
+
+ _font->drawString(&_gui->_screen, it->name, it->bbox.left + kMenuLeftMargin, it->bbox.top + (_gui->_builtInFonts ? 2 : 1), it->bbox.width(), color);
+ }
+
+ g_system->copyRectToScreen(_gui->_screen.getPixels(), _gui->_screen.pitch, 0, 0, _gui->_screen.w, kMenuHeight);
+}
+
+void Menu::renderSubmenu(MenuItem *menu) {
+ Common::Rect *r = &menu->subbbox;
+
+ if (r->width() == 0 || r->height() == 0)
+ return;
+
+ Design::drawFilledRect(&_gui->_screen, *r, kColorWhite, _gui->_patterns, kPatternSolid);
+ Design::drawRect(&_gui->_screen, *r, 1, kColorBlack, _gui->_patterns, kPatternSolid);
+ Design::drawVLine(&_gui->_screen, r->right + 1, r->top + 3, r->bottom + 1, 1, kColorBlack, _gui->_patterns, kPatternSolid);
+ Design::drawHLine(&_gui->_screen, r->left + 3, r->right + 1, r->bottom + 1, 1, kColorBlack, _gui->_patterns, kPatternSolid);
+
+ int x = r->left + kMenuDropdownPadding;
+ int y = r->top + 1;
+ for (uint i = 0; i < menu->subitems.size(); i++) {
+ Common::String text(menu->subitems[i]->text);
+ Common::String acceleratorText(getAcceleratorString(menu->subitems[i], ""));
+ int accelX = r->right - 25;
+
+ int color = kColorBlack;
+ if (i == (uint)_activeSubItem && !text.empty() && menu->subitems[i]->enabled) {
+ color = kColorWhite;
+ Common::Rect trect(r->left, y - (_gui->_builtInFonts ? 1 : 0), r->right, y + _font->getFontHeight());
+
+ Design::drawFilledRect(&_gui->_screen, trect, kColorBlack, _gui->_patterns, kPatternSolid);
+ }
+
+ if (!text.empty()) {
+ Graphics::Surface *s = &_gui->_screen;
+ int tx = x, ty = y;
+
+ if (!menu->subitems[i]->enabled) {
+ s = &_tempSurface;
+ tx = 0;
+ ty = 0;
+ accelX -= x;
+
+ _tempSurface.fillRect(Common::Rect(0, 0, _tempSurface.w, _tempSurface.h), kColorGreen);
+ }
+
+ _font->drawString(s, text, tx, ty, r->width(), color);
+
+ if (!acceleratorText.empty())
+ _font->drawString(s, acceleratorText, accelX, ty, r->width(), color);
+
+ if (!menu->subitems[i]->enabled) {
+ // I am lazy to extend drawString() with plotProc as a parameter, so
+ // fake it here
+ for (int ii = 0; ii < _tempSurface.h; ii++) {
+ const byte *src = (const byte *)_tempSurface.getBasePtr(0, ii);
+ byte *dst = (byte *)_gui->_screen.getBasePtr(x, y+ii);
+ byte pat = _gui->_patterns[kPatternCheckers2 - 1][ii % 8];
+ for (int j = 0; j < r->width(); j++) {
+ if (*src != kColorGreen && (pat & (1 << (7 - (x + j) % 8))))
+ *dst = *src;
+ src++;
+ dst++;
+ }
+ }
+ }
+ } else { // Delimiter
+ Design::drawHLine(&_gui->_screen, r->left + 1, r->right - 1, y + kMenuDropdownItemHeight / 2, 1, kColorBlack, _gui->_patterns, kPatternStripes);
+ }
+
+ y += kMenuDropdownItemHeight;
+ }
+
+ g_system->copyRectToScreen(_gui->_screen.getBasePtr(r->left, r->top), _gui->_screen.pitch, r->left, r->top, r->width() + 3, r->height() + 3);
+}
+
+bool Menu::mouseClick(int x, int y) {
+ if (_bbox.contains(x, y)) {
+ if (!_menuActivated)
+ _screenCopy.copyFrom(_gui->_screen);
+
+ for (uint i = 0; i < _items.size(); i++)
+ if (_items[i]->bbox.contains(x, y)) {
+ if ((uint)_activeItem == i)
+ return false;
+
+ if (_activeItem != -1) { // Restore background
+ Common::Rect r(_items[_activeItem]->subbbox);
+ r.right += 3;
+ r.bottom += 3;
+
+ _gui->_screen.copyRectToSurface(_screenCopy, r.left, r.top, r);
+ g_system->copyRectToScreen(_gui->_screen.getBasePtr(r.left, r.top), _gui->_screen.pitch, r.left, r.top, r.width() + 1, r.height() + 1);
+ }
+
+ _activeItem = i;
+ _activeSubItem = -1;
+ _menuActivated = true;
+
+ return true;
+ }
+ } else if (_menuActivated && _items[_activeItem]->subbbox.contains(x, y)) {
+ MenuItem *it = _items[_activeItem];
+ int numSubItem = (y - it->subbbox.top) / kMenuDropdownItemHeight;
+
+ if (numSubItem != _activeSubItem) {
+ _activeSubItem = numSubItem;
+
+ renderSubmenu(_items[_activeItem]);
+ }
+ } else if (_menuActivated && _activeItem != -1) {
+ _activeSubItem = -1;
+
+ renderSubmenu(_items[_activeItem]);
+ }
+
+ return false;
+}
+
+bool Menu::mouseMove(int x, int y) {
+ if (_menuActivated)
+ if (mouseClick(x, y))
+ return true;
+
+ return false;
+}
+
+bool Menu::mouseRelease(int x, int y) {
+ if (_menuActivated) {
+ _menuActivated = false;
+
+ if (_activeItem != -1 && _activeSubItem != -1 && _items[_activeItem]->subitems[_activeSubItem]->enabled)
+ executeCommand(_items[_activeItem]->subitems[_activeSubItem]);
+
+ _activeItem = -1;
+ _activeSubItem = -1;
+
+ return true;
+ }
+
+ return false;
+}
+
+void Menu::executeCommand(MenuSubItem *subitem) {
+ switch(subitem->action) {
+ case kMenuActionAbout:
+ case kMenuActionNew:
+ case kMenuActionOpen:
+ case kMenuActionClose:
+ case kMenuActionSave:
+ case kMenuActionSaveAs:
+ case kMenuActionRevert:
+ case kMenuActionQuit:
+
+ case kMenuActionUndo:
+ _gui->actionUndo();
+ break;
+ case kMenuActionCut:
+ _gui->actionCut();
+ break;
+ case kMenuActionCopy:
+ _gui->actionCopy();
+ break;
+ case kMenuActionPaste:
+ _gui->actionPaste();
+ break;
+ case kMenuActionClear:
+ _gui->actionClear();
+ break;
+
+ case kMenuActionCommand:
+ _gui->_engine->processTurn(&subitem->text, NULL);
+ break;
+
+ default:
+ warning("Unknown action: %d", subitem->action);
+
+ }
+}
+
+void Menu::processMenuShortCut(byte flags, uint16 ascii) {
+ ascii = tolower(ascii);
+
+ if (flags & (Common::KBD_CTRL | Common::KBD_META)) {
+ for (uint i = 0; i < _items.size(); i++)
+ for (uint j = 0; j < _items[i]->subitems.size(); j++)
+ if (_items[i]->subitems[j]->enabled && tolower(_items[i]->subitems[j]->shortcut) == ascii) {
+ executeCommand(_items[i]->subitems[j]);
+ break;
+ }
+ }
+}
+
+void Menu::enableCommand(int menunum, int action, bool state) {
+ for (uint i = 0; i < _items[menunum]->subitems.size(); i++)
+ if (_items[menunum]->subitems[i]->action == action)
+ _items[menunum]->subitems[i]->enabled = state;
+}
+
+void Menu::disableAllMenus() {
+ for (uint i = 1; i < _items.size(); i++) // Leave About menu on
+ for (uint j = 0; j < _items[i]->subitems.size(); j++)
+ _items[i]->subitems[j]->enabled = false;
+}
+
+} // End of namespace Wage
diff --git a/engines/wage/menu.h b/engines/wage/menu.h
new file mode 100644
index 0000000000..3550356bc6
--- /dev/null
+++ b/engines/wage/menu.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.
+ *
+ * MIT License:
+ *
+ * Copyright (c) 2009 Alexei Svitkine, Eugene Sandulenko
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#ifndef WAGE_MENU_H
+#define WAGE_MENU_H
+
+namespace Wage {
+
+struct MenuItem;
+struct MenuSubItem;
+
+enum {
+ kFontStyleBold = 1,
+ kFontStyleItalic = 2,
+ kFontStyleUnderline = 4,
+ kFontStyleOutline = 8,
+ kFontStyleShadow = 16,
+ kFontStyleCondensed = 32,
+ kFontStyleExtended = 64
+};
+
+enum {
+ kMenuAbout = 0,
+ kMenuFile = 1,
+ kMenuEdit = 2,
+ kMenuCommands = 3,
+ kMenuWeapons = 4
+};
+
+enum {
+ kMenuActionAbout,
+ kMenuActionNew,
+ kMenuActionOpen,
+ kMenuActionClose,
+ kMenuActionSave,
+ kMenuActionSaveAs,
+ kMenuActionRevert,
+ kMenuActionQuit,
+
+ kMenuActionUndo,
+ kMenuActionCut,
+ kMenuActionCopy,
+ kMenuActionPaste,
+ kMenuActionClear,
+
+ kMenuActionCommand
+};
+
+class Menu {
+public:
+ Menu(Gui *gui);
+ ~Menu();
+
+ void render();
+ bool mouseClick(int x, int y);
+ bool mouseRelease(int x, int y);
+ bool mouseMove(int x, int y);
+
+ void regenCommandsMenu();
+ void regenWeaponsMenu();
+ void processMenuShortCut(byte flags, uint16 ascii);
+ void enableCommand(int menunum, int action, bool state);
+ void disableAllMenus();
+
+ bool _menuActivated;
+ Common::Rect _bbox;
+
+private:
+ Gui *_gui;
+ Graphics::Surface _screenCopy;
+ Graphics::Surface _tempSurface;
+
+private:
+ const Graphics::Font *getMenuFont();
+ const char *getAcceleratorString(MenuSubItem *item, const char *prefix);
+ int calculateMenuWidth(MenuItem *menu);
+ void calcMenuBounds(MenuItem *menu);
+ void renderSubmenu(MenuItem *menu);
+ void createCommandsMenu(MenuItem *menu);
+ void createWeaponsMenu(MenuItem *menu);
+ void executeCommand(MenuSubItem *subitem);
+
+ Common::Array<MenuItem *> _items;
+ MenuItem *_weapons;
+ MenuItem *_commands;
+
+ const Graphics::Font *_font;
+
+ int _activeItem;
+ int _activeSubItem;
+};
+
+} // End of namespace Wage
+
+#endif
diff --git a/engines/wage/module.mk b/engines/wage/module.mk
new file mode 100644
index 0000000000..21316bbf83
--- /dev/null
+++ b/engines/wage/module.mk
@@ -0,0 +1,29 @@
+MODULE := engines/wage
+
+MODULE_OBJS := \
+ combat.o \
+ debugger.o \
+ design.o \
+ detection.o \
+ dialog.o \
+ entities.o \
+ gui.o \
+ gui-console.o \
+ menu.o \
+ randomhat.o \
+ script.o \
+ sound.o \
+ util.o \
+ wage.o \
+ world.o
+
+MODULE_DIRS += \
+ engines/wage
+
+# This module can be built as a plugin
+ifeq ($(ENABLE_WAGE), DYNAMIC_PLUGIN)
+PLUGIN := 1
+endif
+
+# Include common rules
+include $(srcdir)/rules.mk
diff --git a/engines/wage/randomhat.cpp b/engines/wage/randomhat.cpp
new file mode 100644
index 0000000000..9371140398
--- /dev/null
+++ b/engines/wage/randomhat.cpp
@@ -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.
+ *
+ * MIT License:
+ *
+ * Copyright (c) 2009 Alexei Svitkine, Eugene Sandulenko
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include "common/random.h"
+
+#include "common/hashmap.h"
+#include "wage/randomhat.h"
+
+namespace Wage {
+
+void RandomHat::addTokens(int type, int count) {
+ _tokens.setVal(type, _tokens.getVal(type, 0) + count);
+}
+
+int RandomHat::countTokens() {
+ int count = 0;
+ for (Common::HashMap<int, int>::const_iterator it = _tokens.begin(); it != _tokens.end(); ++it)
+ count += it->_value;
+
+ return count;
+}
+
+int RandomHat::drawToken() {
+ int total = countTokens();
+ if (total > 0) {
+ int random = _rnd->getRandomNumber(total - 1);
+ int count = 0;
+ for (Common::HashMap<int, int>::iterator it = _tokens.begin(); it != _tokens.end(); ++it) {
+ if (random >= count && random < count + it->_value) {
+ it->_value--;
+ return it->_key;
+ }
+ count += it->_value;
+ }
+ }
+ return kTokNone;
+}
+
+} // End of namespace Wage
diff --git a/engines/wage/randomhat.h b/engines/wage/randomhat.h
new file mode 100644
index 0000000000..254cd2ae8d
--- /dev/null
+++ b/engines/wage/randomhat.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.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * MIT License:
+ *
+ * Copyright (c) 2009 Alexei Svitkine, Eugene Sandulenko
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#ifndef WAGE_RANDOMHAT_H
+#define WAGE_RANDOMHAT_H
+
+namespace Wage {
+
+enum {
+ kTokWeapons = -400,
+ kTokMagic = -300,
+ kTokRun = -200,
+ kTokOffer = -100,
+ kTokNone = -100000
+};
+
+class RandomHat {
+public:
+ RandomHat(Common::RandomSource *rnd) : _rnd(rnd) {}
+
+ void addTokens(int type, int count);
+ int drawToken();
+
+private:
+ Common::RandomSource *_rnd;
+ Common::HashMap<int, int> _tokens;
+
+ int countTokens();
+};
+
+} // End of namespace Wage
+
+#endif
diff --git a/engines/wage/script.cpp b/engines/wage/script.cpp
new file mode 100644
index 0000000000..294c08ed82
--- /dev/null
+++ b/engines/wage/script.cpp
@@ -0,0 +1,1175 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * MIT License:
+ *
+ * Copyright (c) 2009 Alexei Svitkine, Eugene Sandulenko
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include "wage/wage.h"
+#include "wage/entities.h"
+#include "wage/script.h"
+#include "wage/world.h"
+
+#include "common/stream.h"
+
+namespace Wage {
+
+Common::String Script::Operand::toString() {
+ switch(_type) {
+ case NUMBER:
+ return Common::String::format("%d", _value.number);
+ case STRING:
+ case TEXT_INPUT:
+ return *_value.string;
+ case OBJ:
+ return _value.obj->toString();
+ case CHR:
+ return _value.chr->toString();
+ case SCENE:
+ return _value.scene->toString();
+ case CLICK_INPUT:
+ return _value.inputClick->toString();
+ default:
+ error("Unhandled operand type: _type");
+ }
+}
+
+Script::Script(Common::SeekableReadStream *data) : _data(data) {
+ _engine = NULL;
+ _world = NULL;
+
+ _loopCount = 0;
+ _inputText = NULL;
+ _inputClick = NULL;
+
+ _handled = false;
+
+ convertToText();
+}
+
+Script::~Script() {
+ for (uint i = 0; i < _scriptText.size(); i++) {
+ delete _scriptText[i];
+ }
+
+ delete _data;
+}
+
+void Script::print() {
+ for (uint i = 0; i < _scriptText.size(); i++) {
+ debug(4, "%d [%04x]: %s", i, _scriptText[i]->offset, _scriptText[i]->line.c_str());
+ }
+}
+
+void Script::printLine(int offset) {
+ for (uint i = 0; i < _scriptText.size(); i++)
+ if (_scriptText[i]->offset >= offset) {
+ debug(4, "%d [%04x]: %s", i, _scriptText[i]->offset, _scriptText[i]->line.c_str());
+ break;
+ }
+}
+
+bool Script::execute(World *world, int loopCount, Common::String *inputText, Designed *inputClick, WageEngine *engine) {
+ _world = world;
+ _loopCount = loopCount;
+ _inputText = inputText;
+ _inputClick = inputClick;
+ _engine = engine;
+ _handled = false;
+ Common::String input;
+
+ if (inputText)
+ input = *inputText;
+
+ _data->seek(12);
+ while (_data->pos() < _data->size()) {
+ printLine(_data->pos());
+
+ byte command = _data->readByte();
+
+ switch(command) {
+ case 0x80: // IF
+ processIf();
+ break;
+ case 0x87: // EXIT
+ debug(6, "exit at offset %d", _data->pos() - 1);
+
+ return true;
+ case 0x89: // MOVE
+ {
+ Scene *currentScene = _world->_player->_currentScene;
+ processMove();
+ if (_world->_player->_currentScene != currentScene)
+ return true;
+ break;
+ }
+ case 0x8B: // PRINT
+ {
+ Operand *op = readOperand();
+ // TODO check op type is string or number, or something good...
+ _handled = true;
+ _engine->appendText(op->toString().c_str());
+ delete op;
+ byte d = _data->readByte();
+ if (d != 0xFD)
+ warning("Operand 0x8B (PRINT) End Byte != 0xFD");
+ break;
+ }
+ case 0x8C: // SOUND
+ {
+ Operand *op = readOperand();
+ // TODO check op type is string.
+ _handled = true;
+ _engine->playSound(op->toString());
+ delete op;
+ byte d = _data->readByte();
+ if (d != 0xFD)
+ warning("Operand 0x8B (PRINT) End Byte != 0xFD");
+ break;
+ }
+ case 0x8E: // LET
+ processLet();
+ break;
+ case 0x95: // MENU
+ {
+ Operand *op = readStringOperand(); // allows empty menu
+ // TODO check op type is string.
+ _engine->setMenu(op->toString());
+ delete op;
+ byte d = _data->readByte();
+ if (d != 0xFD)
+ warning("Operand 0x8B (PRINT) End Byte != 0xFD");
+ }
+ case 0x88: // END
+ break;
+ default:
+ debug(0, "Unknown opcode: %d", _data->pos());
+ }
+ }
+
+ if (_world->_globalScript != this) {
+ debug(1, "Executing global script...");
+ bool globalHandled = _world->_globalScript->execute(_world, _loopCount, &input, _inputClick, _engine);
+ if (globalHandled)
+ _handled = true;
+ } else if (!input.empty()) {
+ if (input.contains("north")) {
+ _handled = _engine->handleMoveCommand(NORTH, "north");
+ } else if (input.contains("east")) {
+ _handled = _engine->handleMoveCommand(EAST, "east");
+ } else if (input.contains("south")) {
+ _handled = _engine->handleMoveCommand(SOUTH, "south");
+ } else if (input.contains("west")) {
+ _handled = _engine->handleMoveCommand(WEST, "west");
+ } else if (input.hasPrefix("take ")) {
+ _handled = _engine->handleTakeCommand(&input.c_str()[5]);
+ } else if (input.hasPrefix("get ")) {
+ _handled = _engine->handleTakeCommand(&input.c_str()[4]);
+ } else if (input.hasPrefix("pick up ")) {
+ _handled = _engine->handleTakeCommand(&input.c_str()[8]);
+ } else if (input.hasPrefix("drop ")) {
+ _handled = _engine->handleDropCommand(&input.c_str()[5]);
+ } else if (input.hasPrefix("aim ")) {
+ _handled = _engine->handleAimCommand(&input.c_str()[4]);
+ } else if (input.hasPrefix("wear ")) {
+ _handled = _engine->handleWearCommand(&input.c_str()[5]);
+ } else if (input.hasPrefix("put on ")) {
+ _handled = _engine->handleWearCommand(&input.c_str()[7]);
+ } else if (input.hasPrefix("offer ")) {
+ _handled = _engine->handleOfferCommand(&input.c_str()[6]);
+ } else if (input.contains("look")) {
+ _handled = _engine->handleLookCommand();
+ } else if (input.contains("inventory")) {
+ _handled = _engine->handleInventoryCommand();
+ } else if (input.contains("status")) {
+ _handled = _engine->handleStatusCommand();
+ } else if (input.contains("rest") || input.equals("wait")) {
+ _handled = _engine->handleRestCommand();
+ } else if (_engine->getOffer() != NULL && input.contains("accept")) {
+ _handled = _engine->handleAcceptCommand();
+ } else {
+ Chr *player = _world->_player;
+ ObjArray *weapons = player->getWeapons(true);
+ for (ObjArray::const_iterator weapon = weapons->begin(); weapon != weapons->end(); ++weapon) {
+ if (_engine->tryAttack(*weapon, input)) {
+ _handled = _engine->handleAttack(*weapon);
+ break;
+ }
+ }
+
+ delete weapons;
+ }
+ // TODO: weapons, offer, etc...
+ } else if (_inputClick->_classType == OBJ) {
+ Obj *obj = (Obj *)_inputClick;
+ if (obj->_type != Obj::IMMOBILE_OBJECT) {
+ _engine->takeObj(obj);
+ } else {
+ _engine->appendText(obj->_clickMessage.c_str());
+ }
+
+ _handled = true;
+ }
+
+ return _handled;
+}
+
+Script::Operand *Script::readOperand() {
+ byte operandType = _data->readByte();
+
+ debug(7, "%x: readOperand: 0x%x", _data->pos(), operandType);
+
+ Context *cont = &_world->_player->_context;
+ switch (operandType) {
+ case 0xA0: // TEXT$
+ return new Operand(_inputText, TEXT_INPUT);
+ case 0xA1:
+ return new Operand(_inputClick, CLICK_INPUT);
+ case 0xC0: // STORAGE@
+ return new Operand(_world->_storageScene, SCENE);
+ case 0xC1: // SCENE@
+ return new Operand(_world->_player->_currentScene, SCENE);
+ case 0xC2: // PLAYER@
+ return new Operand(_world->_player, CHR);
+ case 0xC3: // MONSTER@
+ return new Operand(_engine->getMonster(), CHR);
+ case 0xC4: // RANDOMSCN@
+ return new Operand(_world->_orderedScenes[_engine->_rnd->getRandomNumber(_world->_orderedScenes.size())], SCENE);
+ case 0xC5: // RANDOMCHR@
+ return new Operand(_world->_orderedChrs[_engine->_rnd->getRandomNumber(_world->_orderedChrs.size())], CHR);
+ case 0xC6: // RANDOMOBJ@
+ return new Operand(_world->_orderedObjs[_engine->_rnd->getRandomNumber(_world->_orderedObjs.size())], OBJ);
+ case 0xB0: // VISITS#
+ return new Operand(cont->_visits, NUMBER);
+ case 0xB1: // RANDOM# for Star Trek, but VISITS# for some other games?
+ return new Operand(1 + _engine->_rnd->getRandomNumber(100), NUMBER);
+ case 0xB5: // RANDOM# // A random number between 1 and 100.
+ return new Operand(1 + _engine->_rnd->getRandomNumber(100), NUMBER);
+ case 0xB2: // LOOP#
+ return new Operand(_loopCount, NUMBER);
+ case 0xB3: // VICTORY#
+ return new Operand(cont->_kills, NUMBER);
+ case 0xB4: // BADCOPY#
+ return new Operand(0, NUMBER); // \?\?\??
+ case 0xFF:
+ {
+ // user variable
+ int value = _data->readByte();
+
+ // TODO: Verify that we're using the right index.
+ return new Operand(cont->_userVariables[value - 1], NUMBER);
+ }
+ case 0xD0:
+ return new Operand(cont->_statVariables[PHYS_STR_BAS], NUMBER);
+ case 0xD1:
+ return new Operand(cont->_statVariables[PHYS_HIT_BAS], NUMBER);
+ case 0xD2:
+ return new Operand(cont->_statVariables[PHYS_ARM_BAS], NUMBER);
+ case 0xD3:
+ return new Operand(cont->_statVariables[PHYS_ACC_BAS], NUMBER);
+ case 0xD4:
+ return new Operand(cont->_statVariables[SPIR_STR_BAS], NUMBER);
+ case 0xD5:
+ return new Operand(cont->_statVariables[SPIR_HIT_BAS], NUMBER);
+ case 0xD6:
+ return new Operand(cont->_statVariables[SPIR_ARM_BAS], NUMBER);
+ case 0xD7:
+ return new Operand(cont->_statVariables[SPIR_ACC_BAS], NUMBER);
+ case 0xD8:
+ return new Operand(cont->_statVariables[PHYS_SPE_BAS], NUMBER);
+ case 0xE0:
+ return new Operand(cont->_statVariables[PHYS_STR_CUR], NUMBER);
+ case 0xE1:
+ return new Operand(cont->_statVariables[PHYS_HIT_CUR], NUMBER);
+ case 0xE2:
+ return new Operand(cont->_statVariables[PHYS_ARM_CUR], NUMBER);
+ case 0xE3:
+ return new Operand(cont->_statVariables[PHYS_ACC_CUR], NUMBER);
+ case 0xE4:
+ return new Operand(cont->_statVariables[SPIR_STR_CUR], NUMBER);
+ case 0xE5:
+ return new Operand(cont->_statVariables[SPIR_HIT_CUR], NUMBER);
+ case 0xE6:
+ return new Operand(cont->_statVariables[SPIR_ARM_CUR], NUMBER);
+ case 0xE7:
+ return new Operand(cont->_statVariables[SPIR_ACC_CUR], NUMBER);
+ case 0xE8:
+ return new Operand(cont->_statVariables[PHYS_SPE_CUR], NUMBER);
+ default:
+ if (operandType >= 0x20 && operandType < 0x80) {
+ _data->seek(-1, SEEK_CUR);
+ return readStringOperand();
+ } else {
+ debug("Dunno what %x is (index=%d)!\n", operandType, _data->pos()-1);
+ }
+ return NULL;
+ }
+}
+
+void Script::assign(byte operandType, int uservar, uint16 value) {
+ Context *cont = &_world->_player->_context;
+
+ switch (operandType) {
+ case 0xFF:
+ cont->_userVariables[uservar - 1] = value;
+ break;
+ case 0xD0:
+ cont->_statVariables[PHYS_STR_BAS] = value;
+ break;
+ case 0xD1:
+ cont->_statVariables[PHYS_HIT_BAS] = value;
+ break;
+ case 0xD2:
+ cont->_statVariables[PHYS_ARM_BAS] = value;
+ break;
+ case 0xD3:
+ cont->_statVariables[PHYS_ACC_BAS] = value;
+ break;
+ case 0xD4:
+ cont->_statVariables[SPIR_STR_BAS] = value;
+ break;
+ case 0xD5:
+ cont->_statVariables[SPIR_HIT_BAS] = value;
+ break;
+ case 0xD6:
+ cont->_statVariables[SPIR_ARM_BAS] = value;
+ break;
+ case 0xD7:
+ cont->_statVariables[SPIR_ACC_BAS] = value;
+ break;
+ case 0xD8:
+ cont->_statVariables[PHYS_SPE_BAS] = value;
+ break;
+ case 0xE0:
+ cont->_statVariables[PHYS_STR_CUR] = value;
+ break;
+ case 0xE1:
+ cont->_statVariables[PHYS_HIT_CUR] = value;
+ break;
+ case 0xE2:
+ cont->_statVariables[PHYS_ARM_CUR] = value;
+ break;
+ case 0xE3:
+ cont->_statVariables[PHYS_ACC_CUR] = value;
+ break;
+ case 0xE4:
+ cont->_statVariables[SPIR_STR_CUR] = value;
+ break;
+ case 0xE5:
+ cont->_statVariables[SPIR_HIT_CUR] = value;
+ break;
+ case 0xE6:
+ cont->_statVariables[SPIR_ARM_CUR] = value;
+ break;
+ case 0xE7:
+ cont->_statVariables[SPIR_ACC_CUR] = value;
+ break;
+ case 0xE8:
+ cont->_statVariables[PHYS_SPE_CUR] = value;
+ break;
+ default:
+ debug("No idea what I'm supposed to assign! (%x at %d)!\n", operandType, _data->pos()-1);
+ }
+}
+
+Script::Operand *Script::readStringOperand() {
+ Common::String *str;
+ bool allDigits = true;
+
+ str = new Common::String();
+
+ while (true) {
+ byte c = _data->readByte();
+ if (c >= 0x20 && c < 0x80)
+ *str += c;
+ else
+ break;
+ if (c < '0' || c > '9')
+ allDigits = false;
+ }
+ _data->seek(-1, SEEK_CUR);
+
+ if (allDigits && !str->empty()) {
+ int r = atol(str->c_str());
+ delete str;
+
+ return new Operand(r, NUMBER);
+ } else {
+ // TODO: This string could be a room name or something like that.
+ return new Operand(str, STRING);
+ }
+}
+
+const char *Script::readOperator() {
+ byte cmd = _data->readByte();
+
+ debug(7, "readOperator: 0x%x", cmd);
+ switch (cmd) {
+ case 0x81:
+ return "=";
+ case 0x82:
+ return "<";
+ case 0x83:
+ return ">";
+ case 0x8f:
+ return "+";
+ case 0x90:
+ return "-";
+ case 0x91:
+ return "*";
+ case 0x92:
+ return "/";
+ case 0x93:
+ return "==";
+ case 0x94:
+ return ">>";
+ case 0xfd:
+ return ";";
+ default:
+ warning("UNKNOWN OP %x", cmd);
+ }
+ return NULL;
+}
+
+void Script::processIf() {
+ int logicalOp = 0; // 0 => initial, 1 => and, 2 => or
+ bool result = true;
+ bool done = false;
+
+ do {
+ Operand *lhs = readOperand();
+ const char *op = readOperator();
+ Operand *rhs = readOperand();
+
+ bool condResult = eval(lhs, op, rhs);
+
+ delete lhs;
+ delete rhs;
+
+ if (logicalOp == 1) {
+ result = (result && condResult);
+ } else if (logicalOp == 2) {
+ result = (result || condResult);
+ } else { // logicalOp == 0
+ result = condResult;
+ }
+
+ byte logical = _data->readByte();
+
+ if (logical == 0x84) {
+ logicalOp = 1; // and
+ } else if (logical == 0x85) {
+ logicalOp = 2; // or
+ } else if (logical == 0xFE) {
+ done = true; // then
+ }
+ } while (!done);
+
+ if (result == false) {
+ skipBlock();
+ }
+}
+
+void Script::skipIf() {
+ do {
+ Operand *lhs = readOperand();
+ readOperator();
+ Operand *rhs = readOperand();
+
+ delete lhs;
+ delete rhs;
+ } while (_data->readByte() != 0xFE);
+}
+
+void Script::skipBlock() {
+ int nesting = 1;
+
+ while (true) {
+ byte op = _data->readByte();
+
+ if (_data->eos())
+ return;
+
+ if (op == 0x80) { // IF
+ nesting++;
+ skipIf();
+ } else if (op == 0x88 || op == 0x87) { // END or EXIT
+ nesting--;
+ if (nesting == 0) {
+ return;
+ }
+ } else switch (op) {
+ case 0x8B: // PRINT
+ case 0x8C: // SOUND
+ case 0x8E: // LET
+ case 0x95: // MENU
+ while (_data->readByte() != 0xFD)
+ ;
+ }
+ }
+}
+
+enum {
+ kCompEqNumNum,
+ kCompEqObjScene,
+ kCompEqChrScene,
+ kCompEqObjChr,
+ kCompEqChrChr,
+ kCompEqSceneScene,
+ kCompEqStringTextInput,
+ kCompEqTextInputString,
+ kCompEqNumberTextInput,
+ kCompEqTextInputNumber,
+ kCompLtNumNum,
+ kCompLtStringTextInput,
+ kCompLtTextInputString,
+ kCompLtObjChr,
+ kCompLtChrObj,
+ kCompLtObjScene,
+ kCompGtNumNum,
+ kCompGtStringString,
+ kCompGtChrScene,
+ kMoveObjChr,
+ kMoveObjScene,
+ kMoveChrScene
+};
+
+static const char *typeNames[] = {
+ "OBJ",
+ "CHR",
+ "SCENE",
+ "NUMBER",
+ "STRING",
+ "CLICK_INPUT",
+ "TEXT_INPUT"
+};
+
+static const char *operandTypeToStr(int type) {
+ if (type < 0 || type > 6)
+ return "UNKNOWN";
+
+ return typeNames[type];
+}
+
+struct Comparator {
+ char op;
+ OperandType o1;
+ OperandType o2;
+ int cmp;
+} static comparators[] = {
+ { '=', NUMBER, NUMBER, kCompEqNumNum },
+ { '=', OBJ, SCENE, kCompEqObjScene },
+ { '=', CHR, SCENE, kCompEqChrScene },
+ { '=', OBJ, CHR, kCompEqObjChr },
+ { '=', CHR, CHR, kCompEqChrChr },
+ { '=', SCENE, SCENE, kCompEqSceneScene },
+ { '=', STRING, TEXT_INPUT, kCompEqStringTextInput },
+ { '=', TEXT_INPUT, STRING, kCompEqTextInputString },
+ { '=', NUMBER, TEXT_INPUT, kCompEqNumberTextInput },
+ { '=', TEXT_INPUT, NUMBER, kCompEqTextInputNumber },
+
+ { '<', NUMBER, NUMBER, kCompLtNumNum },
+ { '<', STRING, TEXT_INPUT, kCompLtStringTextInput },
+ { '<', TEXT_INPUT, STRING, kCompLtTextInputString },
+ { '<', OBJ, CHR, kCompLtObjChr },
+ { '<', CHR, OBJ, kCompLtChrObj },
+ { '<', OBJ, SCENE, kCompLtObjScene },
+ { '<', CHR, CHR, kCompEqChrChr }, // Same logic as =
+ { '<', SCENE, SCENE, kCompEqSceneScene },
+
+ { '>', NUMBER, NUMBER, kCompGtNumNum },
+ { '>', TEXT_INPUT, STRING, kCompLtTextInputString }, // Same logic as <
+ //FIXME: this prevents the below cases from working due to exact
+ //matches taking precedence over conversions...
+ //{ '>', STRING, STRING, kCompGtStringString }, // Same logic as <
+ { '>', OBJ, CHR, kCompLtObjChr }, // Same logic as <
+ { '>', OBJ, SCENE, kCompLtObjScene }, // Same logic as <
+ { '>', CHR, SCENE, kCompGtChrScene },
+
+ { 'M', OBJ, CHR, kMoveObjChr },
+ { 'M', OBJ, SCENE, kMoveObjScene },
+ { 'M', CHR, SCENE, kMoveChrScene },
+ { 0, OBJ, OBJ, 0 }
+};
+
+bool Script::compare(Operand *o1, Operand *o2, int comparator) {
+ switch(comparator) {
+ case kCompEqNumNum:
+ return o1->_value.number == o2->_value.number;
+ case kCompEqObjScene:
+ for (ObjList::const_iterator it = o2->_value.scene->_objs.begin(); it != o2->_value.scene->_objs.end(); ++it)
+ if (*it == o1->_value.obj)
+ return true;
+ return false;
+ case kCompEqChrScene:
+ for (ChrList::const_iterator it = o2->_value.scene->_chrs.begin(); it != o2->_value.scene->_chrs.end(); ++it)
+ if (*it == o1->_value.chr)
+ return true;
+ return false;
+ case kCompEqObjChr:
+ for (ObjArray::const_iterator it = o2->_value.chr->_inventory.begin(); it != o2->_value.chr->_inventory.end(); ++it)
+ if (*it == o1->_value.obj)
+ return true;
+ return false;
+ case kCompEqChrChr:
+ return o1->_value.chr == o2->_value.chr;
+ case kCompEqSceneScene:
+ return o1->_value.scene == o2->_value.scene;
+ case kCompEqStringTextInput:
+ if (_inputText == NULL) {
+ return false;
+ } else {
+ Common::String s1(*_inputText), s2(*o1->_value.string);
+ s1.toLowercase();
+ s2.toLowercase();
+
+ return s1.contains(s2);
+ }
+ case kCompEqTextInputString:
+ return compare(o2, o1, kCompEqStringTextInput);
+ case kCompEqNumberTextInput:
+ if (_inputText == NULL) {
+ return false;
+ } else {
+ Common::String s1(*_inputText), s2(o1->toString());
+ s1.toLowercase();
+ s2.toLowercase();
+
+ return s1.contains(s2);
+ }
+ case kCompEqTextInputNumber:
+ if (_inputText == NULL) {
+ return false;
+ } else {
+ Common::String s1(*_inputText), s2(o2->toString());
+ s1.toLowercase();
+ s2.toLowercase();
+
+ return s1.contains(s2);
+ }
+ case kCompLtNumNum:
+ return o1->_value.number < o2->_value.number;
+ case kCompLtStringTextInput:
+ return !compare(o1, o2, kCompEqStringTextInput);
+ case kCompLtTextInputString:
+ return !compare(o2, o1, kCompEqStringTextInput);
+ case kCompLtObjChr:
+ return o1->_value.obj->_currentOwner != o2->_value.chr;
+ case kCompLtChrObj:
+ return compare(o2, o1, kCompLtObjChr);
+ case kCompLtObjScene:
+ return o1->_value.obj->_currentScene != o2->_value.scene;
+ case kCompGtNumNum:
+ return o1->_value.number > o2->_value.number;
+ case kCompGtStringString:
+ return o1->_value.string == o2->_value.string;
+ case kCompGtChrScene:
+ return (o1->_value.chr != NULL && o1->_value.chr->_currentScene != o2->_value.scene);
+ case kMoveObjChr:
+ if (o1->_value.obj->_currentOwner != o2->_value.chr) {
+ _world->move(o1->_value.obj, o2->_value.chr);
+ _handled = true; // TODO: Is this correct?
+ }
+ break;
+ case kMoveObjScene:
+ if (o1->_value.obj->_currentScene != o2->_value.scene) {
+ _world->move(o1->_value.obj, o2->_value.scene);
+ // Note: This shouldn't call setHandled() - see
+ // Sultan's Palace 'Food and Drink' scene.
+ }
+ break;
+ case kMoveChrScene:
+ _world->move(o1->_value.chr, o2->_value.scene);
+ _handled = true; // TODO: Is this correct?
+ break;
+ }
+
+ return false;
+}
+
+bool Script::evaluatePair(Operand *lhs, const char *op, Operand *rhs) {
+ debug(7, "HANDLING CASE: [lhs=%s/%s, op=%s rhs=%s/%s]",
+ operandTypeToStr(lhs->_type), lhs->toString().c_str(), op, operandTypeToStr(rhs->_type), rhs->toString().c_str());
+
+ for (int cmp = 0; comparators[cmp].op != 0; cmp++) {
+ if (comparators[cmp].op != op[0])
+ continue;
+
+ if (comparators[cmp].o1 == lhs->_type && comparators[cmp].o2 == rhs->_type)
+ return compare(lhs, rhs, comparators[cmp].cmp);
+ }
+
+ // Now, try partial matches.
+ Operand *c1, *c2;
+ for (int cmp = 0; comparators[cmp].op != 0; cmp++) {
+ if (comparators[cmp].op != op[0])
+ continue;
+
+ if (comparators[cmp].o1 == lhs->_type &&
+ (c2 = convertOperand(rhs, comparators[cmp].o2)) != NULL) {
+ bool res = compare(lhs, c2, comparators[cmp].cmp);
+ delete c2;
+ return res;
+ } else if (comparators[cmp].o2 == rhs->_type &&
+ (c1 = convertOperand(lhs, comparators[cmp].o1)) != NULL) {
+ bool res = compare(c1, rhs, comparators[cmp].cmp);
+ delete c1;
+ return res;
+ }
+ }
+
+ // Now, try double conversion.
+ for (int cmp = 0; comparators[cmp].op != 0; cmp++) {
+ if (comparators[cmp].op != op[0])
+ continue;
+
+ if (comparators[cmp].o1 == lhs->_type || comparators[cmp].o2 == rhs->_type)
+ continue;
+
+ if ((c1 = convertOperand(lhs, comparators[cmp].o1)) != NULL) {
+ if ((c2 = convertOperand(rhs, comparators[cmp].o2)) != NULL) {
+ bool res = compare(c1, c2, comparators[cmp].cmp);
+ delete c1;
+ delete c2;
+ return res;
+ }
+ delete c1;
+ }
+ }
+
+ warning("UNHANDLED CASE: [lhs=%s/%s, op=%s rhs=%s/%s]",
+ operandTypeToStr(lhs->_type), lhs->toString().c_str(), op, operandTypeToStr(rhs->_type), rhs->toString().c_str());
+
+ return false;
+}
+
+bool Script::eval(Operand *lhs, const char *op, Operand *rhs) {
+ bool result = false;
+
+ if (lhs->_type == CLICK_INPUT || rhs->_type == CLICK_INPUT) {
+ return evalClickCondition(lhs, op, rhs);
+ } else if (!strcmp(op, "==") || !strcmp(op, ">>")) {
+ // TODO: check if >> can be used for click inputs and if == can be used for other things
+ // exact string match
+ if (lhs->_type == TEXT_INPUT) {
+ if ((rhs->_type != STRING && rhs->_type != NUMBER) || _inputText == NULL) {
+ result = false;
+ } else {
+ result = _inputText->equalsIgnoreCase(rhs->toString());
+ }
+ } else if (rhs->_type == TEXT_INPUT) {
+ if ((lhs->_type != STRING && lhs->_type != NUMBER) || _inputText == NULL) {
+ result = false;
+ } else {
+ result = _inputText->equalsIgnoreCase(lhs->toString());
+ }
+ } else {
+ error("UNHANDLED CASE: [lhs=%s/%s, rhs=%s/%s]",
+ operandTypeToStr(lhs->_type), lhs->toString().c_str(), operandTypeToStr(rhs->_type), rhs->toString().c_str());
+ }
+ if (!strcmp(op, ">>")) {
+ result = !result;
+ }
+
+ return result;
+ } else {
+ return evaluatePair(lhs, op, rhs);
+ }
+
+ return false;
+}
+
+Script::Operand *Script::convertOperand(Operand *operand, int type) {
+ if (operand->_type == type)
+ error("Incorrect conversion to type %d", type);
+
+ if (type == SCENE) {
+ if (operand->_type == STRING || operand->_type == NUMBER) {
+ Common::String key(operand->toString());
+ key.toLowercase();
+ if (_world->_scenes.contains(key))
+ return new Operand(_world->_scenes[key], SCENE);
+ }
+ } else if (type == OBJ) {
+ if (operand->_type == STRING || operand->_type == NUMBER) {
+ Common::String key = operand->toString();
+ key.toLowercase();
+ if (_world->_objs.contains(key))
+ return new Operand(_world->_objs[key], OBJ);
+ } else if (operand->_type == CLICK_INPUT) {
+ if (_inputClick->_classType == OBJ)
+ return new Operand(_inputClick, OBJ);
+ }
+ } else if (type == CHR) {
+ if (operand->_type == STRING || operand->_type == NUMBER) {
+ Common::String key = operand->toString();
+ key.toLowercase();
+ if (_world->_chrs.contains(key))
+ return new Operand(_world->_chrs[key], CHR);
+ } else if (operand->_type == CLICK_INPUT) {
+ if (_inputClick->_classType == CHR)
+ return new Operand(_inputClick, CHR);
+ }
+ }
+
+ return NULL;
+}
+
+bool Script::evalClickEquality(Operand *lhs, Operand *rhs, bool partialMatch) {
+ bool result = false;
+ if (lhs->_value.obj == NULL || rhs->_value.obj == NULL) {
+ result = false;
+ } else if (lhs->_value.obj == rhs->_value.obj) {
+ result = true;
+ } else if (rhs->_type == STRING) {
+ Common::String str = rhs->toString();
+ str.toLowercase();
+
+ debug(9, "evalClickEquality(%s, %s, %d)", lhs->_value.designed->_name.c_str(), rhs->_value.designed->_name.c_str(), partialMatch);
+ debug(9, "l: %s r: %s)", operandTypeToStr(lhs->_type), operandTypeToStr(rhs->_type));
+ debug(9, "class: %d", lhs->_value.inputClick->_classType);
+
+ if (lhs->_value.inputClick->_classType == CHR || lhs->_value.inputClick->_classType == OBJ) {
+ Common::String name = lhs->_value.designed->_name;
+ name.toLowercase();
+
+ if (partialMatch)
+ result = name.contains(str);
+ else
+ result = name.equals(str);
+ }
+
+ debug(9, "result: %d", result);
+ }
+ return result;
+}
+
+bool Script::evalClickCondition(Operand *lhs, const char *op, Operand *rhs) {
+ // TODO: check if >> can be used for click inputs
+ if (strcmp(op, "==") && strcmp(op, "=") && strcmp(op, "<") && strcmp(op, ">")) {
+ error("Unknown operation '%s' for Script::evalClickCondition", op);
+ }
+
+ bool partialMatch = strcmp(op, "==");
+ bool result;
+ if (lhs->_type == CLICK_INPUT) {
+ result = evalClickEquality(lhs, rhs, partialMatch);
+ } else {
+ result = evalClickEquality(rhs, lhs, partialMatch);
+ }
+ if (!strcmp(op, "<") || !strcmp(op, ">")) {
+ // CLICK$<FOO only matches if there was a click
+ if (_inputClick == NULL) {
+ result = false;
+ } else {
+ result = !result;
+ }
+ }
+ return result;
+}
+
+void Script::processMove() {
+ Operand *what = readOperand();
+ byte skip = _data->readByte();
+ if (skip != 0x8a)
+ error("Incorrect operator for MOVE: %02x", skip);
+
+ Operand *to = readOperand();
+
+ skip = _data->readByte();
+ if (skip != 0xfd)
+ error("No end for MOVE: %02x", skip);
+
+ evaluatePair(what, "M", to);
+
+ delete what;
+ delete to;
+}
+
+void Script::processLet() {
+ const char *lastOp = NULL;
+ int16 result = 0;
+ int operandType = _data->readByte();
+ int uservar = 0;
+
+ if (operandType == 0xff) {
+ uservar = _data->readByte();
+ }
+
+ byte eq = _data->readByte(); // skip "=" operator
+
+ debug(7, "processLet: 0x%x, uservar: 0x%x, eq: 0x%x", operandType, uservar, eq);
+
+ do {
+ Operand *operand = readOperand();
+ // TODO assert that value is NUMBER
+ int16 value = operand->_value.number;
+ delete operand;
+ if (lastOp != NULL) {
+ if (lastOp[0] == '+')
+ result += value;
+ else if (lastOp[0] == '-')
+ result -= value;
+ else if (lastOp[0] == '/')
+ result = (int16)(value == 0 ? 0 : result / value);
+ else if (lastOp[0] == '*')
+ result *= value;
+ } else {
+ result = value;
+ }
+ lastOp = readOperator();
+
+ if (lastOp[0] == ';')
+ break;
+ } while (true);
+ //System.out.println("processLet " + buildStringFromOffset(oldIndex - 1, index - oldIndex + 1) + "}");
+
+ assign(operandType, uservar, result);
+}
+
+enum {
+ BLOCK_START,
+ BLOCK_END,
+ STATEMENT,
+ OPERATOR,
+ OPCODE
+};
+
+struct Mapping {
+ const char *cmd;
+ int type;
+} static const mapping[] = {
+ { "IF{", STATEMENT }, // 0x80
+ { "=", OPERATOR },
+ { "<", OPERATOR },
+ { ">", OPERATOR },
+ { "}AND{", OPCODE },
+ { "}OR{", OPCODE },
+ { "\?\?\?(0x86)", OPCODE },
+ { "EXIT\n", BLOCK_END },
+ { "END\n", BLOCK_END }, // 0x88
+ { "MOVE{", STATEMENT },
+ { "}TO{", OPCODE },
+ { "PRINT{", STATEMENT },
+ { "SOUND{", STATEMENT },
+ { "\?\?\?(0x8d)", OPCODE },
+ { "LET{", STATEMENT },
+ { "+", OPERATOR },
+ { "-", OPERATOR }, // 0x90
+ { "*", OPERATOR },
+ { "/", OPERATOR },
+ { "==", OPERATOR },
+ { ">>", OPERATOR },
+ { "MENU{", STATEMENT },
+ { "\?\?\?(0x96)", OPCODE },
+ { "\?\?\?(0x97)", OPCODE },
+ { "\?\?\?(0x98)", OPCODE }, // 0x98
+ { "\?\?\?(0x99)", OPCODE },
+ { "\?\?\?(0x9a)", OPCODE },
+ { "\?\?\?(0x9b)", OPCODE },
+ { "\?\?\?(0x9c)", OPCODE },
+ { "\?\?\?(0x9d)", OPCODE },
+ { "\?\?\?(0x9e)", OPCODE },
+ { "\?\?\?(0x9f)", OPCODE },
+ { "TEXT$", OPCODE }, // 0xa0
+ { "CLICK$", OPCODE },
+ { "\?\?\?(0xa2)", OPCODE },
+ { "\?\?\?(0xa3)", OPCODE },
+ { "\?\?\?(0xa4)", OPCODE },
+ { "\?\?\?(0xa5)", OPCODE },
+ { "\?\?\?(0xa6)", OPCODE },
+ { "\?\?\?(0xa7)", OPCODE },
+ { "\?\?\?(0xa8)", OPCODE }, // 0xa8
+ { "\?\?\?(0xa9)", OPCODE },
+ { "\?\?\?(0xaa)", OPCODE },
+ { "\?\?\?(0xab)", OPCODE },
+ { "\?\?\?(0xac)", OPCODE },
+ { "\?\?\?(0xad)", OPCODE },
+ { "\?\?\?(0xae)", OPCODE },
+ { "\?\?\?(0xaf)", OPCODE },
+ { "VISITS#", OPCODE }, // 0xb0 // The number of scenes the player has visited, including repeated visits.
+ { "RANDOM#", OPCODE }, // RANDOM# for Star Trek, but VISITS# for some other games?
+ { "LOOP#", OPCODE }, // The number of commands the player has given in the current scene.
+ { "VICTORY#", OPCODE }, // The number of characters killed.
+ { "BADCOPY#", OPCODE },
+ { "RANDOM#", OPCODE }, // A random number between 1 and 100.
+ { "\?\?\?(0xb6)", OPCODE },
+ { "\?\?\?(0xb7)", OPCODE },
+ { "\?\?\?(0xb8)", OPCODE }, // 0xb8
+ { "\?\?\?(0xb9)", OPCODE },
+ { "\?\?\?(0xba)", OPCODE },
+ { "\?\?\?(0xbb)", OPCODE },
+ { "\?\?\?(0xbc)", OPCODE },
+ { "\?\?\?(0xbd)", OPCODE },
+ { "\?\?\?(0xbe)", OPCODE },
+ { "\?\?\?(0xbf)", OPCODE },
+ { "STORAGE@", OPCODE }, // 0xc0
+ { "SCENE@", OPCODE },
+ { "PLAYER@", OPCODE },
+ { "MONSTER@", OPCODE },
+ { "RANDOMSCN@", OPCODE },
+ { "RANDOMCHR@", OPCODE },
+ { "RANDOMOBJ@", OPCODE },
+ { "\?\?\?(0xc7)", OPCODE },
+ { "\?\?\?(0xc8)", OPCODE }, // 0xc8
+ { "\?\?\?(0xc9)", OPCODE },
+ { "\?\?\?(0xca)", OPCODE },
+ { "\?\?\?(0xcb)", OPCODE },
+ { "\?\?\?(0xcc)", OPCODE },
+ { "\?\?\?(0xcd)", OPCODE },
+ { "\?\?\?(0xce)", OPCODE },
+ { "\?\?\?(0xcf)", OPCODE },
+ { "PHYS.STR.BAS#", OPCODE }, // 0xd0
+ { "PHYS.HIT.BAS#", OPCODE },
+ { "PHYS.ARM.BAS#", OPCODE },
+ { "PHYS.ACC.BAS#", OPCODE },
+ { "SPIR.STR.BAS#", OPCODE },
+ { "SPIR.HIT.BAS#", OPCODE },
+ { "SPIR.ARM.BAS#", OPCODE },
+ { "SPIR.ACC.BAS#", OPCODE },
+ { "PHYS.SPE.BAS#", OPCODE }, // 0xd8
+ { "\?\?\?(0xd9)", OPCODE },
+ { "\?\?\?(0xda)", OPCODE },
+ { "\?\?\?(0xdb)", OPCODE },
+ { "\?\?\?(0xdc)", OPCODE },
+ { "\?\?\?(0xdd)", OPCODE },
+ { "\?\?\?(0xde)", OPCODE },
+ { "\?\?\?(0xdf)", OPCODE },
+ { "PHYS.STR.CUR#", OPCODE }, // 0xe0
+ { "PHYS.HIT.CUR#", OPCODE },
+ { "PHYS.ARM.CUR#", OPCODE },
+ { "PHYS.ACC.CUR#", OPCODE },
+ { "SPIR.STR.CUR#", OPCODE },
+ { "SPIR.HIT.CUR#", OPCODE },
+ { "SPIR.ARM.CUR#", OPCODE },
+ { "SPIR.ACC.CUR#", OPCODE },
+ { "PHYS.SPE.CUR#", OPCODE }, // 0xe8
+ { "\?\?\?(0xe9)", OPCODE },
+ { "\?\?\?(0xea)", OPCODE },
+ { "\?\?\?(0xeb)", OPCODE },
+ { "\?\?\?(0xec)", OPCODE },
+ { "\?\?\?(0xed)", OPCODE },
+ { "\?\?\?(0xee)", OPCODE },
+ { "\?\?\?(0xef)", OPCODE },
+ { "\?\?\?(0xf0)", OPCODE },
+ { "\?\?\?(0xf1)", OPCODE },
+ { "\?\?\?(0xf2)", OPCODE },
+ { "\?\?\?(0xf3)", OPCODE },
+ { "\?\?\?(0xf4)", OPCODE },
+ { "\?\?\?(0xf5)", OPCODE },
+ { "\?\?\?(0xf6)", OPCODE },
+ { "\?\?\?(0xf7)", OPCODE },
+ { "\?\?\?(0xf8)", OPCODE }, // 0xa8
+ { "\?\?\?(0xf9)", OPCODE },
+ { "\?\?\?(0xfa)", OPCODE },
+ { "\?\?\?(0xfb)", OPCODE },
+ { "\?\?\?(0xfc)", OPCODE },
+ { "}\n", OPCODE },
+ { "}THEN\n", BLOCK_START },
+ { "\?\?\?(0xff)", OPCODE } // Uservar
+};
+
+void Script::convertToText() {
+ _data->seek(12);
+
+ int indentLevel = 0;
+ ScriptText *scr = new ScriptText;
+ scr->offset = _data->pos();
+
+ while(true) {
+ int c = _data->readByte();
+
+ if (_data->eos())
+ break;
+
+ if (c < 0x80) {
+ if (c < 0x20)
+ error("convertToText: Unknown code 0x%02x at %d", c, _data->pos());
+
+ do {
+ scr->line += c;
+ c = _data->readByte();
+ } while (c < 0x80);
+
+ _data->seek(-1, SEEK_CUR);
+ } else if (c == 0xff) {
+ int value = _data->readByte();
+ value -= 1;
+ scr->line += (char)('A' + (value / 9));
+ scr->line += (char)('0' + (value % 9) + 1);
+ scr->line += '#';
+ } else {
+ const char *cmd = mapping[c - 0x80].cmd;
+ int type = mapping[c - 0x80].type;
+
+ if (type == STATEMENT) {
+ for (int i = 0; i < indentLevel; i++)
+ scr->line += ' ';
+ } else if (type == BLOCK_START) {
+ indentLevel += 2;
+ } else if (type == BLOCK_END) {
+ indentLevel -= 2;
+ for (int i = 0; i < indentLevel; i++)
+ scr->line += ' ';
+ }
+
+ scr->line += cmd;
+
+ if (strchr(cmd, '\n')) {
+ scr->line.deleteLastChar();
+
+ _scriptText.push_back(scr);
+
+ scr = new ScriptText;
+ scr->offset = _data->pos();
+ }
+ }
+ }
+
+ if (!scr->line.empty())
+ _scriptText.push_back(scr);
+ else
+ delete scr;
+}
+
+} // End of namespace Wage
diff --git a/engines/wage/script.h b/engines/wage/script.h
new file mode 100644
index 0000000000..de9476228c
--- /dev/null
+++ b/engines/wage/script.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.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * MIT License:
+ *
+ * Copyright (c) 2009 Alexei Svitkine, Eugene Sandulenko
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#ifndef WAGE_SCRIPT_H
+#define WAGE_SCRIPT_H
+
+namespace Wage {
+
+class Script {
+public:
+ Script(Common::SeekableReadStream *data);
+ ~Script();
+
+private:
+ Common::SeekableReadStream *_data;
+
+ WageEngine *_engine;
+ World *_world;
+ int _loopCount;
+ Common::String *_inputText;
+ Designed *_inputClick;
+ bool _handled;
+
+ class Operand {
+ public:
+ union {
+ Obj *obj;
+ Chr *chr;
+ Designed *designed;
+ Scene *scene;
+ int16 number;
+ Common::String *string;
+ Designed *inputClick;
+ } _value;
+ OperandType _type;
+
+ Operand(Obj *value, OperandType type) {
+ _value.obj = value;
+ assert(type == OBJ);
+ _type = type;
+ }
+
+ Operand(Chr *value, OperandType type) {
+ _value.chr = value;
+ assert(type == CHR);
+ _type = type;
+ }
+
+ Operand(Scene *value, OperandType type) {
+ _value.scene = value;
+ assert(type == SCENE);
+ _type = type;
+ }
+
+ Operand(int value, OperandType type) {
+ _value.number = value;
+ assert(type == NUMBER);
+ _type = type;
+ }
+
+ Operand(Common::String *value, OperandType type) {
+ _value.string = value;
+ assert(type == STRING || type == TEXT_INPUT);
+ _type = type;
+ }
+
+ Operand(Designed *value, OperandType type) {
+ _value.inputClick = value;
+ assert(type == CLICK_INPUT);
+ _type = type;
+ }
+
+ ~Operand() {
+ if (_type == STRING)
+ delete _value.string;
+ }
+
+ Common::String toString();
+ };
+
+ struct ScriptText {
+ int offset;
+ Common::String line;
+ };
+
+public:
+ void print();
+ void printLine(int offset);
+ bool execute(World *world, int loopCount, Common::String *inputText, Designed *inputClick, WageEngine *engine);
+
+private:
+ Operand *readOperand();
+ Operand *readStringOperand();
+ const char *readOperator();
+ void processIf();
+ void skipBlock();
+ void skipIf();
+ bool compare(Operand *o1, Operand *o2, int comparator);
+ bool eval(Operand *lhs, const char *op, Operand *rhs);
+ bool evaluatePair(Operand *lhs, const char *op, Operand *rhs);
+ Operand *convertOperand(Operand *operand, int type);
+ bool evalClickCondition(Operand *lhs, const char *op, Operand *rhs);
+ bool evalClickEquality(Operand *lhs, Operand *rhs, bool partialMatch);
+ void processMove();
+ void processLet();
+
+ void assign(byte operandType, int uservar, uint16 value);
+
+ void convertToText();
+
+public:
+ Common::Array<ScriptText *> _scriptText;
+};
+
+} // End of namespace Wage
+
+#endif
diff --git a/engines/wage/sound.cpp b/engines/wage/sound.cpp
new file mode 100644
index 0000000000..bcb274cab9
--- /dev/null
+++ b/engines/wage/sound.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.
+ *
+ * MIT License:
+ *
+ * Copyright (c) 2009 Alexei Svitkine, Eugene Sandulenko
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include "common/stream.h"
+
+#include "wage/wage.h"
+#include "wage/sound.h"
+
+namespace Wage {
+
+static const int8 deltas[] = { 0,-49,-36,-25,-16,-9,-4,-1,0,1,4,9,16,25,36,49 };
+
+Sound::Sound(Common::String name, Common::SeekableReadStream *data) : _name(name) {
+ int size = data->size() - 20;
+ _data = (byte *)calloc(2 * size, 1);
+
+ data->skip(20); // Skip header
+
+ byte value = 0x80;
+ for (int i = 0; i < size; i++) {
+ byte d = data->readByte();
+ value += deltas[d & 0xf];
+ _data[i * 2] = value;
+ value += deltas[(d >> 4) & 0xf];
+ _data[i * 2 + 1] = value;
+ }
+}
+
+Sound::~Sound() {
+ free(_data);
+}
+
+void WageEngine::playSound(Common::String soundName) {
+ warning("STUB: WageEngine::playSound(%s)", soundName.c_str());
+}
+
+void WageEngine::updateSoundTimerForScene(Scene *scene, bool firstTime) {
+ //warning("STUB: WageEngine::updateSoundTimerForScene()");
+}
+
+
+} // End of namespace Wage
diff --git a/engines/wage/sound.h b/engines/wage/sound.h
new file mode 100644
index 0000000000..eccfe33170
--- /dev/null
+++ b/engines/wage/sound.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.
+ *
+ * MIT License:
+ *
+ * Copyright (c) 2009 Alexei Svitkine, Eugene Sandulenko
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#ifndef WAGE_SOUND_H
+#define WAGE_SOUND_H
+
+namespace Wage {
+
+class Sound {
+public:
+ Sound(Common::String name, Common::SeekableReadStream *data);
+ ~Sound();
+
+ Common::String _name;
+ byte *_data;
+};
+
+} // End of namespace Wage
+
+#endif
diff --git a/engines/wage/util.cpp b/engines/wage/util.cpp
new file mode 100644
index 0000000000..f31a83ca04
--- /dev/null
+++ b/engines/wage/util.cpp
@@ -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.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * MIT License:
+ *
+ * Copyright (c) 2009 Alexei Svitkine, Eugene Sandulenko
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include "common/stream.h"
+
+#include "wage/wage.h"
+
+namespace Wage {
+
+Common::String readPascalString(Common::SeekableReadStream *in) {
+ Common::String s;
+ char *buf;
+ int len;
+ int i;
+
+ len = in->readByte();
+ buf = (char *)malloc(len + 1);
+ for (i = 0; i < len; i++) {
+ buf[i] = in->readByte();
+ if (buf[i] == 0x0d)
+ buf[i] = '\n';
+ }
+
+ buf[i] = 0;
+
+ s = buf;
+ free(buf);
+
+ return s;
+}
+
+Common::Rect *readRect(Common::SeekableReadStream *in) {
+ int x1, y1, x2, y2;
+
+ y1 = in->readUint16BE();
+ x1 = in->readUint16BE();
+ y2 = in->readUint16BE() + 4;
+ x2 = in->readUint16BE() + 4;
+
+ debug(9, "readRect: %d, %d, %d, %d", x1, y1, x2, y2);
+
+ return new Common::Rect(x1, y1, x2, y2);
+}
+
+const char *getIndefiniteArticle(const Common::String &word) {
+ switch (word[0]) {
+ case 'a': case 'A':
+ case 'e': case 'E':
+ case 'i': case 'I':
+ case 'o': case 'O':
+ case 'u': case 'U':
+ return "an ";
+ }
+ return "a ";
+}
+
+enum {
+ GENDER_MALE = 0,
+ GENDER_FEMALE = 1,
+ GENDER_NEUTRAL = 2
+};
+
+const char *prependGenderSpecificPronoun(int gender) {
+ if (gender == GENDER_MALE)
+ return "his ";
+ else if (gender == GENDER_FEMALE)
+ return "her ";
+ else
+ return "its ";
+}
+
+const char *getGenderSpecificPronoun(int gender, bool capitalize) {
+ if (gender == GENDER_MALE)
+ return capitalize ? "He" : "he";
+ else if (gender == GENDER_FEMALE)
+ return capitalize ? "She" : "she";
+ else
+ return capitalize ? "It" : "it";
+}
+
+} // End of namespace Wage
diff --git a/engines/wage/wage.cpp b/engines/wage/wage.cpp
new file mode 100644
index 0000000000..e0299c8da2
--- /dev/null
+++ b/engines/wage/wage.cpp
@@ -0,0 +1,515 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * MIT License:
+ *
+ * Copyright (c) 2009 Alexei Svitkine, Eugene Sandulenko
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include "common/debug-channels.h"
+#include "common/error.h"
+#include "common/events.h"
+#include "common/system.h"
+
+#include "engines/engine.h"
+#include "engines/util.h"
+
+#include "wage/wage.h"
+#include "wage/entities.h"
+#include "wage/gui.h"
+#include "wage/dialog.h"
+#include "wage/script.h"
+#include "wage/world.h"
+
+namespace Wage {
+
+WageEngine::WageEngine(OSystem *syst, const ADGameDescription *desc) : Engine(syst), _gameDescription(desc) {
+ _rnd = new Common::RandomSource("wage");
+
+ _aim = -1;
+ _opponentAim = -1;
+ _temporarilyHidden = false;
+ _isGameOver = false;
+ _monster = NULL;
+ _running = NULL;
+ _lastScene = NULL;
+
+ _loopCount = 0;
+ _turn = 0;
+
+ _commandWasQuick = false;
+
+ _shouldQuit = false;
+
+ _gui = NULL;
+ _world = NULL;
+ _console = NULL;
+ _offer = NULL;
+
+ _resManager = NULL;
+
+ debug("WageEngine::WageEngine()");
+}
+
+WageEngine::~WageEngine() {
+ debug("WageEngine::~WageEngine()");
+
+ DebugMan.clearAllDebugChannels();
+ delete _world;
+ delete _resManager;
+ delete _gui;
+ delete _rnd;
+ delete _console;
+}
+
+Common::Error WageEngine::run() {
+ debug("WageEngine::init");
+
+ initGraphics(512, 342, true);
+
+ // Create debugger console. It requires GFX to be initialized
+ _console = new Console(this);
+
+ _debugger = new Debugger(this);
+
+ // Your main event loop should be (invoked from) here.
+ _resManager = new Common::MacResManager();
+ if (!_resManager->open(getGameFile()))
+ error("Could not open %s as a resource fork", getGameFile());
+
+ _world = new World(this);
+
+ if (!_world->loadWorld(_resManager))
+ return Common::kNoGameDataFoundError;
+
+ _gui = new Gui(this);
+
+ _temporarilyHidden = true;
+ performInitialSetup();
+ Common::String input("look");
+ processTurn(&input, NULL);
+ _temporarilyHidden = false;
+
+ _shouldQuit = false;
+
+ while (!_shouldQuit) {
+ _debugger->onFrame();
+
+ processEvents();
+
+ _gui->draw();
+ g_system->updateScreen();
+ g_system->delayMillis(50);
+ }
+
+ return Common::kNoError;
+}
+
+void WageEngine::processEvents() {
+ Common::Event event;
+
+ while (_eventMan->pollEvent(event)) {
+ switch (event.type) {
+ case Common::EVENT_QUIT:
+ if (saveDialog())
+ _shouldQuit = true;
+ break;
+ case Common::EVENT_MOUSEMOVE:
+ _gui->mouseMove(event.mouse.x, event.mouse.y);
+ break;
+ case Common::EVENT_LBUTTONDOWN:
+ _gui->mouseDown(event.mouse.x, event.mouse.y);
+ break;
+ case Common::EVENT_LBUTTONUP:
+ {
+ Designed *obj = _gui->mouseUp(event.mouse.x, event.mouse.y);
+ if (obj != NULL)
+ processTurn(NULL, obj);
+ }
+ break;
+ case Common::EVENT_KEYDOWN:
+ switch (event.kbd.keycode) {
+ case Common::KEYCODE_BACKSPACE:
+ if (!_inputText.empty()) {
+ _inputText.deleteLastChar();
+ _gui->drawInput();
+ }
+ break;
+
+ case Common::KEYCODE_RETURN:
+ if (_inputText.empty())
+ break;
+
+ processTurn(&_inputText, NULL);
+ _gui->disableUndo();
+ break;
+
+ default:
+ if (event.kbd.ascii == '~') {
+ _debugger->attach();
+ break;
+ }
+
+ if (event.kbd.flags & (Common::KBD_ALT | Common::KBD_CTRL | Common::KBD_META)) {
+ if (event.kbd.ascii >= 0x20 && event.kbd.ascii <= 0x7f) {
+ _gui->processMenuShortCut(event.kbd.flags, event.kbd.ascii);
+ }
+ break;
+ }
+
+ if (event.kbd.ascii >= 0x20 && event.kbd.ascii <= 0x7f) {
+ _inputText += (char)event.kbd.ascii;
+ _gui->drawInput();
+ }
+
+ break;
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+void WageEngine::setMenu(Common::String menu) {
+ _world->_commandsMenu = menu;
+
+ _gui->regenCommandsMenu();
+}
+
+void WageEngine::appendText(const char *str) {
+ _gui->appendText(str);
+
+ _inputText.clear();
+}
+
+void WageEngine::gameOver() {
+ DialogButtonArray buttons;
+
+ buttons.push_back(new DialogButton("OK", 66, 67, 68, 28));
+
+ Dialog gameOverDialog(_gui, 199, _world->_gameOverMessage->c_str(), &buttons, 0);
+
+ gameOverDialog.run();
+
+ doClose();
+
+ _gui->disableAllMenus();
+ _gui->enableNewGameMenus();
+ _gui->_menuDirty = true;
+}
+
+bool WageEngine::saveDialog() {
+ DialogButtonArray buttons;
+
+ buttons.push_back(new DialogButton("No", 19, 67, 68, 28));
+ buttons.push_back(new DialogButton("Yes", 112, 67, 68, 28));
+ buttons.push_back(new DialogButton("Cancel", 205, 67, 68, 28));
+
+ Dialog save(_gui, 291, "Save changes before closing?", &buttons, 1);
+
+ int button = save.run();
+
+ if (button == 2) // Cancel
+ return false;
+
+ if (button == 1)
+ saveGame();
+
+ doClose();
+
+ return true;
+}
+
+void WageEngine::saveGame() {
+ warning("STUB: saveGame()");
+}
+
+void WageEngine::performInitialSetup() {
+ debug(5, "Resetting Objs: %d", _world->_orderedObjs.size());
+ for (uint i = 0; i < _world->_orderedObjs.size() - 1; i++)
+ _world->move(_world->_orderedObjs[i], _world->_storageScene, true);
+
+ _world->move(_world->_orderedObjs[_world->_orderedObjs.size() - 1], _world->_storageScene);
+
+ debug(5, "Resetting Chrs: %d", _world->_orderedChrs.size());
+ for (uint i = 0; i < _world->_orderedChrs.size() - 1; i++)
+ _world->move(_world->_orderedChrs[i], _world->_storageScene, true);
+
+ _world->move(_world->_orderedChrs[_world->_orderedChrs.size() - 1], _world->_storageScene);
+
+ debug(5, "Resetting Owners: %d", _world->_orderedObjs.size());
+ for (uint i = 0; i < _world->_orderedObjs.size(); i++) {
+ Obj *obj = _world->_orderedObjs[i];
+ if (!obj->_sceneOrOwner.equalsIgnoreCase(STORAGESCENE)) {
+ Common::String location = obj->_sceneOrOwner;
+ location.toLowercase();
+ if (_world->_scenes.contains(location)) {
+ _world->move(obj, _world->_scenes[location]);
+ } else {
+ if (!_world->_chrs.contains(location)) {
+ // Note: PLAYER@ is not a valid target here.
+ warning("Couldn't move %s to %s", obj->_name.c_str(), obj->_sceneOrOwner.c_str());
+ } else {
+ // TODO: Add check for max items.
+ _world->move(obj, _world->_chrs[location]);
+ }
+ }
+ }
+ }
+
+ bool playerPlaced = false;
+ for (uint i = 0; i < _world->_orderedChrs.size(); i++) {
+ Chr *chr = _world->_orderedChrs[i];
+ if (!chr->_initialScene.equalsIgnoreCase(STORAGESCENE)) {
+ Common::String key = chr->_initialScene;
+ key.toLowercase();
+ if (_world->_scenes.contains(key) && _world->_scenes[key] != NULL) {
+ _world->move(chr, _world->_scenes[key]);
+
+ if (chr->_playerCharacter)
+ debug(0, "Initial scene: %s", key.c_str());
+ } else {
+ _world->move(chr, _world->getRandomScene());
+ }
+ if (chr->_playerCharacter) {
+ playerPlaced = true;
+ }
+ }
+ chr->wearObjs();
+ }
+ if (!playerPlaced) {
+ _world->move(_world->_player, _world->getRandomScene());
+ }
+}
+
+void WageEngine::doClose() {
+ warning("STUB: doClose()");
+}
+
+Scene *WageEngine::getSceneByName(Common::String &location) {
+ Scene *scene;
+ if (location.equals("random@")) {
+ scene = _world->getRandomScene();
+ } else {
+ scene = _world->_scenes[location];
+ }
+ return scene;
+}
+
+void WageEngine::onMove(Designed *what, Designed *from, Designed *to) {
+ Chr *player = _world->_player;
+ Scene *currentScene = player->_currentScene;
+ if (currentScene == _world->_storageScene && !_temporarilyHidden) {
+ if (!_isGameOver) {
+ _isGameOver = true;
+ gameOver();
+ }
+ return;
+ }
+
+ if (from == currentScene || to == currentScene ||
+ (what->_classType == CHR && ((Chr *)what)->_currentScene == currentScene) ||
+ (what->_classType == OBJ && ((Obj *)what)->_currentScene == currentScene))
+ _gui->setSceneDirty();
+
+ if ((from == player || to == player) && !_temporarilyHidden)
+ _gui->regenWeaponsMenu();
+
+ if (what != player && what->_classType == CHR) {
+ Chr *chr = (Chr *)what;
+ if (to == _world->_storageScene) {
+ int returnTo = chr->_returnTo;
+ if (returnTo != Chr::RETURN_TO_STORAGE) {
+ Common::String returnToSceneName;
+ if (returnTo == Chr::RETURN_TO_INITIAL_SCENE) {
+ returnToSceneName = chr->_initialScene;
+ returnToSceneName.toLowercase();
+ } else {
+ returnToSceneName = "random@";
+ }
+ Scene *scene = getSceneByName(returnToSceneName);
+ if (scene != NULL && scene != _world->_storageScene) {
+ _world->move(chr, scene);
+ // To avoid sleeping twice, return if the above move command would cause a sleep.
+ if (scene == currentScene)
+ return;
+ }
+ }
+ } else if (to == player->_currentScene) {
+ if (getMonster() == NULL) {
+ _monster = chr;
+ encounter(player, chr);
+ }
+ }
+ }
+ if (!_temporarilyHidden) {
+ if (to == currentScene || from == currentScene) {
+ redrawScene();
+ g_system->delayMillis(100);
+ }
+ }
+}
+
+void WageEngine::redrawScene() {
+ Scene *currentScene = _world->_player->_currentScene;
+
+ if (currentScene != NULL) {
+ bool firstTime = (_lastScene != currentScene);
+
+ updateSoundTimerForScene(currentScene, firstTime);
+ }
+}
+
+void WageEngine::processTurnInternal(Common::String *textInput, Designed *clickInput) {
+ Scene *playerScene = _world->_player->_currentScene;
+ if (playerScene == _world->_storageScene)
+ return;
+
+ bool shouldEncounter = false;
+
+ if (playerScene != _lastScene) {
+ _loopCount = 0;
+ _lastScene = playerScene;
+ _monster = NULL;
+ _running = NULL;
+ _offer = NULL;
+
+ for (ChrList::const_iterator it = playerScene->_chrs.begin(); it != playerScene->_chrs.end(); ++it) {
+ if (!(*it)->_playerCharacter) {
+ _monster = *it;
+ shouldEncounter = true;
+ break;
+ }
+ }
+ }
+
+ bool monsterWasNull = (_monster == NULL);
+ Script *script = playerScene->_script != NULL ? playerScene->_script : _world->_globalScript;
+ bool handled = script->execute(_world, _loopCount++, textInput, clickInput, this);
+
+ playerScene = _world->_player->_currentScene;
+
+ if (playerScene == _world->_storageScene)
+ return;
+
+ if (playerScene != _lastScene) {
+ _temporarilyHidden = true;
+ _gui->clearOutput();
+ regen();
+ Common::String input("look");
+ processTurnInternal(&input, NULL);
+ redrawScene();
+ _temporarilyHidden = false;
+ } else if (_loopCount == 1) {
+ redrawScene();
+ if (shouldEncounter && getMonster() != NULL) {
+ encounter(_world->_player, _monster);
+ }
+ } else if (textInput != NULL && !handled) {
+ if (monsterWasNull && getMonster() != NULL)
+ return;
+
+ const char *rant = _rnd->getRandomNumber(1) ? "What?" : "Huh?";
+
+ appendText(rant);
+ _commandWasQuick = true;
+ }
+}
+
+void WageEngine::processTurn(Common::String *textInput, Designed *clickInput) {
+ _commandWasQuick = false;
+ Scene *prevScene = _world->_player->_currentScene;
+ Chr *prevMonster = getMonster();
+ Common::String input;
+
+ if (textInput)
+ input = *textInput;
+
+ input.toLowercase();
+ if (input.equals("e"))
+ input = "east";
+ else if (input.equals("w"))
+ input = "west";
+ else if (input.equals("n"))
+ input = "north";
+ else if (input.equals("s"))
+ input = "south";
+
+ processTurnInternal(&input, clickInput);
+ Scene *playerScene = _world->_player->_currentScene;
+
+ if (prevScene != playerScene && playerScene != _world->_storageScene) {
+ if (prevMonster != NULL) {
+ bool followed = false;
+ if (getMonster() == NULL) {
+ // TODO: adjacent scenes doesn't contain up/down etc... verify that monsters can't follow these...
+ if (_world->scenesAreConnected(playerScene, prevMonster->_currentScene)) {
+ int chance = _rnd->getRandomNumber(255);
+ followed = (chance < prevMonster->_followsOpponent);
+ }
+ }
+
+ char buf[512];
+
+ if (followed) {
+ snprintf(buf, 512, "%s%s follows you.", prevMonster->getDefiniteArticle(true), prevMonster->_name.c_str());
+ appendText(buf);
+
+ _world->move(prevMonster, playerScene);
+ } else {
+ snprintf(buf, 512, "You escape %s%s.", prevMonster->getDefiniteArticle(false), prevMonster->_name.c_str());
+ appendText(buf);
+ }
+ }
+ }
+ if (!_commandWasQuick && getMonster() != NULL) {
+ performCombatAction(getMonster(), _world->_player);
+ }
+
+ _inputText.clear();
+ _gui->appendText("");
+}
+
+
+} // End of namespace Wage
diff --git a/engines/wage/wage.h b/engines/wage/wage.h
new file mode 100644
index 0000000000..8ca306aea3
--- /dev/null
+++ b/engines/wage/wage.h
@@ -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.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * MIT License:
+ *
+ * Copyright (c) 2009 Alexei Svitkine, Eugene Sandulenko
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#ifndef WAGE_WAGE_H
+#define WAGE_WAGE_H
+
+#include "engines/engine.h"
+#include "common/debug.h"
+#include "common/endian.h"
+#include "common/rect.h"
+#include "common/macresman.h"
+#include "common/random.h"
+
+#include "wage/debugger.h"
+
+struct ADGameDescription;
+
+namespace Wage {
+
+class Console;
+class Chr;
+class Designed;
+class Dialog;
+class Gui;
+class Obj;
+class Scene;
+class World;
+
+typedef Common::Array<Obj *> ObjArray;
+typedef Common::Array<Chr *> ChrArray;
+typedef Common::List<Obj *> ObjList;
+typedef Common::List<Chr *> ChrList;
+
+enum OperandType {
+ OBJ = 0,
+ CHR = 1,
+ SCENE = 2,
+ NUMBER = 3,
+ STRING = 4,
+ CLICK_INPUT = 5,
+ TEXT_INPUT = 6,
+ UNKNOWN = 100
+};
+
+enum Directions {
+ NORTH = 0,
+ SOUTH = 1,
+ EAST = 2,
+ WEST = 3
+};
+
+// our engine debug levels
+enum {
+ kWageDebugExample = 1 << 0,
+ kWageDebugExample2 = 1 << 1
+ // next new level must be 1 << 2 (4)
+ // the current limitation is 32 debug levels (1 << 31 is the last one)
+};
+
+enum {
+ kColorBlack = 0,
+ kColorGray = 1,
+ kColorWhite = 2,
+ kColorGreen = 3
+};
+
+Common::String readPascalString(Common::SeekableReadStream *in);
+Common::Rect *readRect(Common::SeekableReadStream *in);
+const char *getIndefiniteArticle(const Common::String &word);
+const char *prependGenderSpecificPronoun(int gender);
+const char *getGenderSpecificPronoun(int gender, bool capitalize);
+
+
+typedef Common::Array<byte *> Patterns;
+
+class WageEngine : public Engine {
+ friend class Dialog;
+public:
+ WageEngine(OSystem *syst, const ADGameDescription *gameDesc);
+ ~WageEngine();
+
+ virtual bool hasFeature(EngineFeature f) const;
+
+ virtual Common::Error run();
+
+ bool canLoadGameStateCurrently();
+ bool canSaveGameStateCurrently();
+
+ const char *getGameFile() const;
+ void processTurn(Common::String *textInput, Designed *clickInput);
+ void regen();
+
+private:
+ bool loadWorld(Common::MacResManager *resMan);
+ void performInitialSetup();
+ void wearObjs(Chr *chr);
+ void processTurnInternal(Common::String *textInput, Designed *clickInput);
+ void performCombatAction(Chr *npc, Chr *player);
+ int getValidMoveDirections(Chr *npc);
+ void performAttack(Chr *attacker, Chr *victim, Obj *weapon);
+ void performMagic(Chr *attacker, Chr *victim, Obj *magicalObject);
+ void performMove(Chr *chr, int validMoves);
+ void performOffer(Chr *attacker, Chr *victim);
+ void performTake(Chr *npc, Obj *obj);
+ void decrementUses(Obj *obj);
+ bool attackHit(Chr *attacker, Chr *victim, Obj *weapon, int targetIndex);
+ void performHealingMagic(Chr *chr, Obj *magicalObject);
+
+ void doClose();
+ void updateSoundTimerForScene(Scene *scene, bool firstTime);
+
+public:
+ void takeObj(Obj *obj);
+
+ bool handleMoveCommand(Directions dir, const char *dirName);
+ bool handleLookCommand();
+ Common::String *getGroundItemsList(Scene *scene);
+ void appendObjNames(Common::String &str, const ObjArray &objs);
+ bool handleInventoryCommand();
+ bool handleStatusCommand();
+ bool handleRestCommand();
+ bool handleAcceptCommand();
+
+ bool handleTakeCommand(const char *target);
+ bool handleDropCommand(const char *target);
+ bool handleAimCommand(const char *target);
+ bool handleWearCommand(const char *target);
+ bool handleOfferCommand(const char *target);
+
+ void wearObj(Obj *o, int pos);
+
+ bool tryAttack(const Obj *weapon, const Common::String &input);
+ bool handleAttack(Obj *weapon);
+
+ void printPlayerCondition(Chr *player);
+ const char *getPercentMessage(double percent);
+
+public:
+ Common::RandomSource *_rnd;
+
+ Debugger *_debugger;
+
+ Gui *_gui;
+ World *_world;
+
+ Scene *_lastScene;
+ int _loopCount;
+ int _turn;
+ Chr *_monster;
+ Chr *_running;
+ Obj *_offer;
+ int _aim;
+ int _opponentAim;
+ bool _temporarilyHidden;
+ bool _isGameOver;
+ bool _commandWasQuick;
+
+ Common::String _inputText;
+
+ void playSound(Common::String soundName);
+ void setMenu(Common::String soundName);
+ void appendText(const char *str);
+ void gameOver();
+ bool saveDialog();
+ Obj *getOffer();
+ Chr *getMonster();
+ void processEvents();
+ Scene *getSceneByName(Common::String &location);
+ void onMove(Designed *what, Designed *from, Designed *to);
+ void encounter(Chr *player, Chr *chr);
+ void redrawScene();
+ void saveGame();
+
+ virtual GUI::Debugger *getDebugger() { return _debugger; }
+
+private:
+ Console *_console;
+
+ const ADGameDescription *_gameDescription;
+
+ Common::MacResManager *_resManager;
+
+ bool _shouldQuit;
+};
+
+// Example console class
+class Console : public GUI::Debugger {
+public:
+ Console(WageEngine *vm) {}
+ virtual ~Console(void) {}
+};
+
+} // End of namespace Wage
+
+#endif
diff --git a/engines/wage/world.cpp b/engines/wage/world.cpp
new file mode 100644
index 0000000000..40b1555e35
--- /dev/null
+++ b/engines/wage/world.cpp
@@ -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.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * MIT License:
+ *
+ * Copyright (c) 2009 Alexei Svitkine, Eugene Sandulenko
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include "common/file.h"
+
+#include "wage/wage.h"
+#include "wage/entities.h"
+#include "wage/script.h"
+#include "wage/sound.h"
+#include "wage/world.h"
+
+namespace Wage {
+
+World::World(WageEngine *engine) {
+ _storageScene = new Scene;
+ _storageScene->_name = STORAGESCENE;
+
+ _orderedScenes.push_back(_storageScene);
+ _scenes[STORAGESCENE] = _storageScene;
+
+ _gameOverMessage = nullptr;
+ _saveBeforeQuitMessage = nullptr;
+ _saveBeforeCloseMessage = nullptr;
+ _revertMessage = nullptr;
+
+ _globalScript = nullptr;
+ _player = nullptr;
+
+ _weaponMenuDisabled = true;
+
+ _engine = engine;
+}
+
+World::~World() {
+ for (uint i = 0; i < _orderedObjs.size(); i++)
+ delete _orderedObjs[i];
+
+ for (uint i = 0; i < _orderedChrs.size(); i++)
+ delete _orderedChrs[i];
+
+ for (uint i = 0; i < _orderedSounds.size(); i++)
+ delete _orderedSounds[i];
+
+ for (uint i = 0; i < _orderedScenes.size(); i++)
+ delete _orderedScenes[i];
+
+ for (uint i = 0; i < _patterns.size(); i++)
+ free(_patterns[i]);
+
+ delete _globalScript;
+
+ delete _gameOverMessage;
+ delete _saveBeforeQuitMessage;
+ delete _saveBeforeCloseMessage;
+ delete _revertMessage;
+
+}
+
+bool World::loadWorld(Common::MacResManager *resMan) {
+ Common::MacResIDArray resArray;
+ Common::SeekableReadStream *res;
+ Common::MacResIDArray::const_iterator iter;
+
+ if ((resArray = resMan->getResIDArray(MKTAG('G','C','O','D'))).size() == 0)
+ return false;
+
+ // Load global script
+ res = resMan->getResource(MKTAG('G','C','O','D'), resArray[0]);
+ _globalScript = new Script(res);
+
+ // TODO: read creator
+
+ // Load main configuration
+ if ((resArray = resMan->getResIDArray(MKTAG('V','E','R','S'))).size() == 0)
+ return false;
+
+ _name = resMan->getBaseFileName();
+
+ if (resArray.size() > 1)
+ warning("Too many VERS resources");
+
+ if (!resArray.empty()) {
+ debug(3, "Loading version info");
+
+ res = resMan->getResource(MKTAG('V','E','R','S'), resArray[0]);
+
+ res->skip(10);
+ byte b = res->readByte();
+ _weaponMenuDisabled = (b != 0);
+ if (b != 0 && b != 1)
+ error("Unexpected value for weapons menu");
+
+ res->skip(3);
+ _aboutMessage = readPascalString(res);
+
+ if (!scumm_stricmp(resMan->getBaseFileName().c_str(), "Scepters"))
+ res->skip(1); // ????
+
+ _soundLibrary1 = readPascalString(res);
+ _soundLibrary2 = readPascalString(res);
+
+ delete res;
+ }
+
+ Common::String *message;
+ if ((message = loadStringFromDITL(resMan, 2910, 1)) != NULL) {
+ message->trim();
+ debug(2, "_gameOverMessage: %s", message->c_str());
+ _gameOverMessage = message;
+ }
+ if ((message = loadStringFromDITL(resMan, 2480, 3)) != NULL) {
+ message->trim();
+ debug(2, "_saveBeforeQuitMessage: %s", message->c_str());
+ _saveBeforeQuitMessage = message;
+ }
+ if ((message = loadStringFromDITL(resMan, 2490, 3)) != NULL) {
+ message->trim();
+ debug(2, "_saveBeforeCloseMessage: %s", message->c_str());
+ _saveBeforeCloseMessage = message;
+ }
+ if ((message = loadStringFromDITL(resMan, 2940, 2)) != NULL) {
+ message->trim();
+ debug(2, "_revertMessage: %s", message->c_str());
+ _revertMessage = message;
+ }
+
+ // Load scenes
+ resArray = resMan->getResIDArray(MKTAG('A','S','C','N'));
+ debug(3, "Loading %d scenes", resArray.size());
+
+ for (iter = resArray.begin(); iter != resArray.end(); ++iter) {
+ res = resMan->getResource(MKTAG('A','S','C','N'), *iter);
+ Scene *scene = new Scene(resMan->getResName(MKTAG('A','S','C','N'), *iter), res);
+
+ res = resMan->getResource(MKTAG('A','C','O','D'), *iter);
+ if (res != NULL)
+ scene->_script = new Script(res);
+
+ res = resMan->getResource(MKTAG('A','T','X','T'), *iter);
+ if (res != NULL) {
+ scene->_textBounds = readRect(res);
+ scene->_fontType = res->readUint16BE();
+ scene->_fontSize = res->readUint16BE();
+
+ Common::String text;
+ while (res->pos() < res->size()) {
+ char c = res->readByte();
+ if (c == 0x0d)
+ c = '\n';
+ text += c;
+ }
+ scene->_text = text;
+
+ delete res;
+ }
+ addScene(scene);
+ }
+
+ // Load Objects
+ resArray = resMan->getResIDArray(MKTAG('A','O','B','J'));
+ debug(3, "Loading %d objects", resArray.size());
+
+ for (iter = resArray.begin(); iter != resArray.end(); ++iter) {
+ res = resMan->getResource(MKTAG('A','O','B','J'), *iter);
+ addObj(new Obj(resMan->getResName(MKTAG('A','O','B','J'), *iter), res));
+ }
+
+ // Load Characters
+ resArray = resMan->getResIDArray(MKTAG('A','C','H','R'));
+ debug(3, "Loading %d characters", resArray.size());
+
+ for (iter = resArray.begin(); iter != resArray.end(); ++iter) {
+ res = resMan->getResource(MKTAG('A','C','H','R'), *iter);
+ Chr *chr = new Chr(resMan->getResName(MKTAG('A','C','H','R'), *iter), res);
+
+ addChr(chr);
+ // TODO: What if there's more than one player character?
+ if (chr->_playerCharacter)
+ _player = chr;
+ }
+
+ // Load Sounds
+ resArray = resMan->getResIDArray(MKTAG('A','S','N','D'));
+ debug(3, "Loading %d sounds", resArray.size());
+
+ for (iter = resArray.begin(); iter != resArray.end(); ++iter) {
+ res = resMan->getResource(MKTAG('A','S','N','D'), *iter);
+ addSound(new Sound(resMan->getResName(MKTAG('A','S','N','D'), *iter), res));
+ }
+
+ if (!_soundLibrary1.empty()) {
+ loadExternalSounds(_soundLibrary1);
+ }
+ if (!_soundLibrary2.empty()) {
+ loadExternalSounds(_soundLibrary2);
+ }
+
+ // Load Patterns
+ res = resMan->getResource(MKTAG('P','A','T','#'), 900);
+ if (res != NULL) {
+ int count = res->readUint16BE();
+ debug(3, "Loading %d patterns", count);
+
+ for (int i = 0; i < count; i++) {
+ byte *pattern = (byte *)malloc(8);
+
+ res->read(pattern, 8);
+ _patterns.push_back(pattern);
+ }
+
+ delete res;
+ } else {
+ /* Enchanted Scepters did not use the PAT# resource for the textures. */
+ res = resMan->getResource(MKTAG('C','O','D','E'), 1);
+ if (res != NULL) {
+ res->skip(0x55ac);
+ for (int i = 0; i < 29; i++) {
+ byte *pattern = (byte *)malloc(8);
+
+ res->read(pattern, 8);
+ _patterns.push_back(pattern);
+ }
+ }
+ delete res;
+ }
+
+ res = resMan->getResource(MKTAG('M','E','N','U'), 2001);
+ if (res != NULL) {
+ Common::StringArray *menu = readMenu(res);
+ _aboutMenuItemName.clear();
+ Common::String string = menu->operator[](1);
+
+ for (uint i = 0; i < string.size() && string[i] != ';'; i++) // Read token
+ _aboutMenuItemName += string[i];
+
+ delete menu;
+ delete res;
+ }
+ res = resMan->getResource(MKTAG('M','E','N','U'), 2004);
+ if (res != NULL) {
+ Common::StringArray *menu = readMenu(res);
+ _commandsMenuName = menu->operator[](0);
+ _commandsMenu = menu->operator[](1);
+ delete menu;
+ delete res;
+ }
+ res = resMan->getResource(MKTAG('M','E','N','U'), 2005);
+ if (res != NULL) {
+ Common::StringArray *menu = readMenu(res);
+ _weaponsMenuName = menu->operator[](0);
+ delete menu;
+ delete res;
+ }
+ // TODO: Read Apple menu and get the name of that menu item..
+
+ // store global info in state object for use with save/load actions
+ //world.setCurrentState(initialState); // pass off the state object to the world
+
+ return true;
+}
+
+void World::addSound(Sound *sound) {
+ Common::String s = sound->_name;
+ s.toLowercase();
+ _sounds[s] = sound;
+ _orderedSounds.push_back(sound);
+}
+
+Common::StringArray *World::readMenu(Common::SeekableReadStream *res) {
+ res->skip(10);
+ int enableFlags = res->readUint32BE();
+ Common::String menuName = readPascalString(res);
+ Common::String menuItem = readPascalString(res);
+ int menuItemNumber = 1;
+ Common::String menu;
+ byte itemData[4];
+
+ while (!menuItem.empty()) {
+ if (!menu.empty()) {
+ menu += ';';
+ }
+ if ((enableFlags & (1 << menuItemNumber)) == 0) {
+ menu += '(';
+ }
+ menu += menuItem;
+ res->read(itemData, 4);
+ static const char styles[] = {'B', 'I', 'U', 'O', 'S', 'C', 'E', 0};
+ for (int i = 0; styles[i] != 0; i++) {
+ if ((itemData[3] & (1 << i)) != 0) {
+ menu += '<';
+ menu += styles[i];
+ }
+ }
+ if (itemData[1] != 0) {
+ menu += '/';
+ menu += (char)itemData[1];
+ }
+ menuItem = readPascalString(res);
+ menuItemNumber++;
+ }
+
+ Common::StringArray *result = new Common::StringArray;
+ result->push_back(menuName);
+ result->push_back(menu);
+
+ debug(4, "menuName: %s", menuName.c_str());
+ debug(4, "menu: %s", menu.c_str());
+
+ return result;
+}
+
+void World::loadExternalSounds(Common::String fname) {
+ Common::File in;
+
+ in.open(fname);
+ if (!in.isOpen()) {
+ warning("Cannot load sound file <%s>", fname.c_str());
+ return;
+ }
+ in.close();
+
+ Common::MacResManager resMan;
+ resMan.open(fname);
+
+ Common::MacResIDArray resArray;
+ Common::SeekableReadStream *res;
+ Common::MacResIDArray::const_iterator iter;
+
+ resArray = resMan.getResIDArray(MKTAG('A','S','N','D'));
+ for (iter = resArray.begin(); iter != resArray.end(); ++iter) {
+ res = resMan.getResource(MKTAG('A','S','N','D'), *iter);
+ addSound(new Sound(resMan.getResName(MKTAG('A','S','N','D'), *iter), res));
+ }
+}
+
+Common::String *World::loadStringFromDITL(Common::MacResManager *resMan, int resourceId, int itemIndex) {
+ Common::SeekableReadStream *res = resMan->getResource(MKTAG('D','I','T','L'), resourceId);
+ if (res) {
+ int itemCount = res->readSint16BE();
+ for (int i = 0; i <= itemCount; i++) {
+ // int placeholder; short rect[4]; byte flags; pstring str;
+ res->skip(13);
+ Common::String message = readPascalString(res);
+ if (i == itemIndex) {
+ Common::String *msg = new Common::String(message);
+ delete res;
+ return msg;
+ }
+ }
+
+ delete res;
+ }
+
+ return NULL;
+}
+
+static bool invComparator(const Obj *l, const Obj *r) {
+ return l->_index < r->_index;
+}
+
+void World::move(Obj *obj, Chr *chr) {
+ if (obj == NULL)
+ return;
+
+ Designed *from = obj->removeFromCharOrScene();
+ obj->_currentOwner = chr;
+ chr->_inventory.push_back(obj);
+
+ Common::sort(chr->_inventory.begin(), chr->_inventory.end(), invComparator);
+
+ _engine->onMove(obj, from, chr);
+}
+
+static bool objComparator(const Obj *o1, const Obj *o2) {
+ bool o1Immobile = (o1->_type == Obj::IMMOBILE_OBJECT);
+ bool o2Immobile = (o2->_type == Obj::IMMOBILE_OBJECT);
+ if (o1Immobile == o2Immobile) {
+ return o1->_index - o2->_index;
+ }
+ return o1Immobile;
+}
+
+void World::move(Obj *obj, Scene *scene, bool skipSort) {
+ if (obj == NULL)
+ return;
+
+ Designed *from = obj->removeFromCharOrScene();
+ obj->_currentScene = scene;
+ scene->_objs.push_back(obj);
+
+ if (!skipSort)
+ Common::sort(scene->_objs.begin(), scene->_objs.end(), objComparator);
+
+ _engine->onMove(obj, from, scene);
+}
+
+static bool chrComparator(const Chr *l, const Chr *r) {
+ return l->_index < r->_index;
+}
+
+void World::move(Chr *chr, Scene *scene, bool skipSort) {
+ if (chr == NULL)
+ return;
+ Scene *from = chr->_currentScene;
+ if (from == scene)
+ return;
+ if (from != NULL)
+ from->_chrs.remove(chr);
+ scene->_chrs.push_back(chr);
+
+ if (!skipSort)
+ Common::sort(scene->_chrs.begin(), scene->_chrs.end(), chrComparator);
+
+ if (scene == _storageScene) {
+ chr->resetState();
+ } else if (chr->_playerCharacter) {
+ scene->_visited = true;
+ _player->_context._visits++;
+ }
+ chr->_currentScene = scene;
+
+ _engine->onMove(chr, from, scene);
+}
+
+Scene *World::getRandomScene() {
+ // Not including storage:
+ return _orderedScenes[1 + _engine->_rnd->getRandomNumber(_orderedScenes.size() - 2)];
+}
+
+Scene *World::getSceneAt(int x, int y) {
+ for (uint i = 0; i < _orderedScenes.size(); i++) {
+ Scene *scene = _orderedScenes[i];
+
+ if (scene != _storageScene && scene->_worldX == x && scene->_worldY == y) {
+ return scene;
+ }
+ }
+ return NULL;
+}
+
+static const int directionsX[] = { 0, 0, 1, -1 };
+static const int directionsY[] = { -1, 1, 0, 0 };
+
+bool World::scenesAreConnected(Scene *scene1, Scene *scene2) {
+ if (!scene1 || !scene2)
+ return false;
+
+ int x = scene2->_worldX;
+ int y = scene2->_worldY;
+
+ for (int dir = 0; dir < 4; dir++)
+ if (!scene2->_blocked[dir])
+ if (getSceneAt(x + directionsX[dir], y + directionsY[dir]) == scene1)
+ return true;
+
+ return false;
+}
+
+const char *World::getAboutMenuItemName() {
+ static char menu[256];
+
+ *menu = '\0';
+
+ if (_aboutMenuItemName.empty()) {
+ sprintf(menu, "About %s...", _name.c_str());
+ } else { // Replace '@' with name
+ const char *str = _aboutMenuItemName.c_str();
+ const char *pos = strchr(str, '@');
+ if (pos) {
+ strncat(menu, str, (pos - str));
+ strncat(menu, _name.c_str(), 255);
+ strncat(menu, pos + 1, 255);
+ }
+ }
+
+ return menu;
+}
+
+} // End of namespace Wage
diff --git a/engines/wage/world.h b/engines/wage/world.h
new file mode 100644
index 0000000000..e9041139df
--- /dev/null
+++ b/engines/wage/world.h
@@ -0,0 +1,138 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * MIT License:
+ *
+ * Copyright (c) 2009 Alexei Svitkine, Eugene Sandulenko
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#ifndef WAGE_WORLD_H
+#define WAGE_WORLD_H
+
+namespace Wage {
+
+#define STORAGESCENE "STORAGE@"
+
+class Sound;
+
+class World {
+public:
+ World(WageEngine *engine);
+ ~World();
+
+ bool loadWorld(Common::MacResManager *resMan);
+ void loadExternalSounds(Common::String fname);
+ Common::String *loadStringFromDITL(Common::MacResManager *resMan, int resourceId, int itemIndex);
+ void move(Obj *obj, Chr *chr);
+ void move(Obj *obj, Scene *scene, bool skipSort = false);
+ void move(Chr *chr, Scene *scene, bool skipSort = false);
+ Scene *getRandomScene();
+ Scene *getSceneAt(int x, int y);
+ bool scenesAreConnected(Scene *scene1, Scene *scene2);
+ const char *getAboutMenuItemName();
+
+ WageEngine *_engine;
+
+ Common::String _name;
+ Common::String _aboutMessage;
+ Common::String _soundLibrary1;
+ Common::String _soundLibrary2;
+
+ bool _weaponMenuDisabled;
+ Script *_globalScript;
+ Common::HashMap<Common::String, Scene *> _scenes;
+ Common::HashMap<Common::String, Obj *> _objs;
+ Common::HashMap<Common::String, Chr *> _chrs;
+ Common::HashMap<Common::String, Sound *> _sounds;
+ Common::Array<Scene *> _orderedScenes;
+ ObjArray _orderedObjs;
+ ChrArray _orderedChrs;
+ Common::Array<Sound *> _orderedSounds;
+ Patterns _patterns;
+ Scene *_storageScene;
+ Chr *_player;
+ //List<MoveListener> moveListeners;
+
+ Common::String *_gameOverMessage;
+ Common::String *_saveBeforeQuitMessage;
+ Common::String *_saveBeforeCloseMessage;
+ Common::String *_revertMessage;
+
+ Common::String _aboutMenuItemName;
+ Common::String _commandsMenuName;
+ Common::String _commandsMenu;
+ Common::String _weaponsMenuName;
+
+ void addScene(Scene *room) {
+ if (!room->_name.empty()) {
+ Common::String s = room->_name;
+ s.toLowercase();
+ _scenes[s] = room;
+ }
+ _orderedScenes.push_back(room);
+ }
+
+ void addObj(Obj *obj) {
+ Common::String s = obj->_name;
+ s.toLowercase();
+ _objs[s] = obj;
+ obj->_index = _orderedObjs.size();
+ _orderedObjs.push_back(obj);
+ }
+
+ void addChr(Chr *chr) {
+ Common::String s = chr->_name;
+ s.toLowercase();
+ _chrs[s] = chr;
+ chr->_index = _orderedChrs.size();
+ _orderedChrs.push_back(chr);
+ }
+
+ void addSound(Sound *sound);
+
+private:
+ Common::StringArray *readMenu(Common::SeekableReadStream *res);
+};
+
+} // End of namespace Wage
+
+#endif
diff --git a/engines/wintermute/ad/ad_actor.cpp b/engines/wintermute/ad/ad_actor.cpp
index 9d5a35464a..4661d3bca0 100644
--- a/engines/wintermute/ad/ad_actor.cpp
+++ b/engines/wintermute/ad/ad_actor.cpp
@@ -958,13 +958,13 @@ bool AdActor::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack,
stack->correctParams(1);
ScValue *val = stack->pop();
if (!val->isNative()) {
- script->runtimeError("actor.%s method accepts an entity refrence only", name);
+ script->runtimeError("actor.%s method accepts an entity reference only", name);
stack->pushNULL();
return STATUS_OK;
}
AdObject *obj = (AdObject *)val->getNative();
if (!obj || obj->getType() != OBJECT_ENTITY) {
- script->runtimeError("actor.%s method accepts an entity refrence only", name);
+ script->runtimeError("actor.%s method accepts an entity reference only", name);
stack->pushNULL();
return STATUS_OK;
}
diff --git a/engines/wintermute/base/base_engine.h b/engines/wintermute/base/base_engine.h
index 0f4a6b0775..cbf5d92d00 100644
--- a/engines/wintermute/base/base_engine.h
+++ b/engines/wintermute/base/base_engine.h
@@ -74,7 +74,7 @@ public:
static const Timer *getTimer();
static const Timer *getLiveTimer();
static void LOG(bool res, const char *fmt, ...);
- const char *getGameTargetName() const { return _targetName.c_str(); }
+ Common::String getGameTargetName() const { return _targetName; }
Common::String getGameId() const { return _gameId; }
Common::Language getLanguage() const { return _language; }
WMETargetExecutable getTargetExecutable() const {
diff --git a/engines/wintermute/base/base_persistence_manager.cpp b/engines/wintermute/base/base_persistence_manager.cpp
index bb5e0c4091..39462f7a15 100644
--- a/engines/wintermute/base/base_persistence_manager.cpp
+++ b/engines/wintermute/base/base_persistence_manager.cpp
@@ -56,7 +56,7 @@ namespace Wintermute {
#define SAVE_MAGIC_3 0x12564154
//////////////////////////////////////////////////////////////////////////
-BasePersistenceManager::BasePersistenceManager(const char *savePrefix, bool deleteSingleton) {
+BasePersistenceManager::BasePersistenceManager(const Common::String &savePrefix, bool deleteSingleton) {
_saving = false;
_offset = 0;
_saveStream = nullptr;
@@ -91,7 +91,7 @@ BasePersistenceManager::BasePersistenceManager(const char *savePrefix, bool dele
_thumbnailDataSize = 0;
_thumbnailData = nullptr;
- if (savePrefix) {
+ if (savePrefix != "") {
_savePrefix = savePrefix;
} else if (_gameRef) {
_savePrefix = _gameRef->getGameTargetName();
@@ -215,8 +215,8 @@ bool BasePersistenceManager::getSaveExists(int slot) {
}
//////////////////////////////////////////////////////////////////////////
-bool BasePersistenceManager::initSave(const char *desc) {
- if (!desc) {
+bool BasePersistenceManager::initSave(const Common::String &desc) {
+ if (desc == "") {
return STATUS_FAILED;
}
@@ -297,11 +297,11 @@ bool BasePersistenceManager::initSave(const char *desc) {
uint32 dataOffset = _offset +
sizeof(uint32) + // data offset
- sizeof(uint32) + strlen(desc) + 1 + // description
+ sizeof(uint32) + strlen(desc.c_str()) + 1 + // description
sizeof(uint32); // timestamp
putDWORD(dataOffset);
- putString(desc);
+ putString(desc.c_str());
g_system->getTimeAndDate(_savedTimestamp);
putTimeDate(_savedTimestamp);
diff --git a/engines/wintermute/base/base_persistence_manager.h b/engines/wintermute/base/base_persistence_manager.h
index 373d1580de..760b45c907 100644
--- a/engines/wintermute/base/base_persistence_manager.h
+++ b/engines/wintermute/base/base_persistence_manager.h
@@ -63,7 +63,7 @@ public:
uint32 getMaxUsedSlot();
bool getSaveExists(int slot);
bool initLoad(const Common::String &filename);
- bool initSave(const char *desc);
+ bool initSave(const Common::String &desc);
bool getBytes(byte *buffer, uint32 size);
bool putBytes(byte *buffer, uint32 size);
uint32 _offset;
@@ -86,7 +86,7 @@ public:
bool transferCharPtr(const char *name, char **val);
bool transferString(const char *name, Common::String *val);
bool transferVector2(const char *name, Vector2 *val);
- BasePersistenceManager(const char *savePrefix = nullptr, bool deleteSingleton = false);
+ BasePersistenceManager(const Common::String &savePrefix = "", bool deleteSingleton = false);
virtual ~BasePersistenceManager();
bool checkVersion(byte verMajor, byte verMinor, byte verBuild);
diff --git a/engines/wintermute/base/base_sub_frame.cpp b/engines/wintermute/base/base_sub_frame.cpp
index 4388942064..6d0c48ff17 100644
--- a/engines/wintermute/base/base_sub_frame.cpp
+++ b/engines/wintermute/base/base_sub_frame.cpp
@@ -268,7 +268,7 @@ bool BaseSubFrame::draw(int x, int y, BaseObject *registerOwner, float zoomX, fl
Common::Point origin(x, y);
Common::Point newOrigin;
Rect32 oldRect1 = getRect();
- Common::Rect oldRect(oldRect1.top, oldRect1.left, oldRect1.bottom, oldRect1.right);
+ Common::Rect oldRect(oldRect1.left, oldRect1.top, oldRect1.right, oldRect1.bottom);
Common::Point newHotspot;
Graphics::TransformStruct transform = Graphics::TransformStruct(zoomX, zoomY, (uint32)rotate, _hotspotX, _hotspotY, blendMode, alpha, _mirrorX, _mirrorY, 0, 0);
Rect32 newRect = Graphics::TransformTools::newRect(oldRect, transform, &newHotspot);
diff --git a/engines/wintermute/base/file/base_disk_file.cpp b/engines/wintermute/base/file/base_disk_file.cpp
index 82a9e24dfb..d0c51616f4 100644
--- a/engines/wintermute/base/file/base_disk_file.cpp
+++ b/engines/wintermute/base/file/base_disk_file.cpp
@@ -113,13 +113,28 @@ Common::SeekableReadStream *openDiskFile(const Common::String &filename) {
Common::String fixedFilename = filename;
correctSlashes(fixedFilename);
- // Absolute path: TODO: Add specific fallbacks here.
+ // HACK: There are a few games around which mistakenly refer to absolute paths in the scripts.
+ // The original interpreter on Windows usually simply ignores them when it can't find them.
+ // We try to turn the known ones into relative paths.
if (fixedFilename.contains(':')) {
- if (fixedFilename.hasPrefix("c:/windows/fonts/")) { // East Side Story refers to "c:\windows\fonts\framd.ttf"
- fixedFilename = filename.c_str() + 14;
- } else if (fixedFilename.hasPrefix("c:/carol6/svn/data/")) { // Carol Reed 6: Black Circle refers to "c:\carol6\svn\data\sprites\system\help.png"
- fixedFilename = fixedFilename.c_str() + 19;
- } else {
+ const char* const knownPrefixes[] = { // Known absolute paths
+ "c:/windows/fonts/", // East Side Story refers to "c:\windows\fonts\framd.ttf"
+ "c:/carol6/svn/data/", // Carol Reed 6: Black Circle refers to "c:\carol6\svn\data\sprites\system\help.png"
+ "f:/dokument/spel 5/demo/data/" // Carol Reed 5 (non-demo) refers to "f:\dokument\spel 5\demo\data\scenes\credits\op_cred_00\op_cred_00.jpg"
+ };
+
+ bool matched = false;
+
+ for (uint i = 0; i < ARRAYSIZE(knownPrefixes); i++) {
+ if (fixedFilename.hasPrefix(knownPrefixes[i])) {
+ fixedFilename = fixedFilename.c_str() + strlen(knownPrefixes[i]);
+ matched = true;
+ }
+ }
+
+ if (!matched) {
+ // fixedFilename is unchanged and thus still broken, none of the above workarounds worked.
+ // We can only bail out
error("openDiskFile::Absolute path or invalid filename used in %s", filename.c_str());
}
}
diff --git a/engines/wintermute/configure.engine b/engines/wintermute/configure.engine
index bdaf49de3f..55385776de 100644
--- a/engines/wintermute/configure.engine
+++ b/engines/wintermute/configure.engine
@@ -1,3 +1,3 @@
# This file is included from the main "configure" script
# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps]
-add_engine wintermute "Wintermute" yes "" "" "jpeg png zlib vorbis 16bit"
+add_engine wintermute "Wintermute" yes "" "" "jpeg png zlib vorbis 16bit highres"
diff --git a/engines/wintermute/detection.cpp b/engines/wintermute/detection.cpp
index aca682ae99..f77eb5c64d 100644
--- a/engines/wintermute/detection.cpp
+++ b/engines/wintermute/detection.cpp
@@ -75,8 +75,8 @@ static const char *directoryGlobs[] = {
class WintermuteMetaEngine : public AdvancedMetaEngine {
public:
WintermuteMetaEngine() : AdvancedMetaEngine(Wintermute::gameDescriptions, sizeof(WMEGameDescription), Wintermute::wintermuteGames, gameGuiOptions) {
- _singleid = "wintermute";
- _guioptions = GUIO2(GUIO_NOMIDI, GAMEOPTION_SHOW_FPS);
+ _singleId = "wintermute";
+ _guiOptions = GUIO2(GUIO_NOMIDI, GAMEOPTION_SHOW_FPS);
_maxScanDepth = 2;
_directoryGlobs = directoryGlobs;
}
@@ -94,8 +94,8 @@ public:
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();
+ s_fallbackDesc.gameId = "wintermute";
+ s_fallbackDesc.guiOptions = GUIO0();
if (allFiles.contains("data.dcp")) {
Common::String name, caption;
@@ -109,7 +109,7 @@ public:
// Prefix to avoid collisions with actually known games
name = "wmeunk-" + name;
Common::strlcpy(s_fallbackGameIdBuf, name.c_str(), sizeof(s_fallbackGameIdBuf) - 1);
- s_fallbackDesc.gameid = s_fallbackGameIdBuf;
+ s_fallbackDesc.gameId = s_fallbackGameIdBuf;
if (caption != name) {
caption += " (unknown version) ";
char *offset = s_fallbackGameIdBuf + name.size() + 1;
diff --git a/engines/wintermute/detection_tables.h b/engines/wintermute/detection_tables.h
index 25a01766e4..ca30204462 100644
--- a/engines/wintermute/detection_tables.h
+++ b/engines/wintermute/detection_tables.h
@@ -181,10 +181,13 @@ static const WMEGameDescription gameDescriptions[] = {
WME_ENTRY1s("data.dcp", "b3f8b09bb4b05ee3e9d14697525257f9", 59296246), Common::EN_ANY, ADGF_UNSTABLE | ADGF_DEMO, LATEST_VERSION),
// Carol Reed 4 - East Side Story
WME_WINENTRY("carolreed4", "",
- WME_ENTRY1s("data.dcp", "b26377797f060afc2d440d820100c1ce", 529320536), Common::EN_ANY, ADGF_UNSTABLE | ADGF_DEMO, LATEST_VERSION),
+ WME_ENTRY1s("data.dcp", "b26377797f060afc2d440d820100c1ce", 529320536), Common::EN_ANY, ADGF_UNSTABLE, LATEST_VERSION),
// Carol Reed 5 - The Colour of Murder
WME_WINENTRY("carolreed5", "",
WME_ENTRY1s("data.dcp", "3fcfca44209545d0e26774156427b494", 603660415), Common::EN_ANY, ADGF_UNSTABLE, LATEST_VERSION),
+ // Carol Reed 5 - The Colour of Murder (1.0 Demo)
+ WME_WINENTRY("carolreed5", "Demo",
+ WME_ENTRY1s("data.dcp", "27b3efc018ade5ee8f4adf08b4e3c0dd", 92019500), Common::EN_ANY, ADGF_UNSTABLE | ADGF_DEMO, LATEST_VERSION),
// Carol Reed 6 - Black Circle
WME_WINENTRY("carolreed6", "",
WME_ENTRY1s("data.dcp", "0e4c532beecf23d85012168753f41189", 456258147), Common::EN_ANY, ADGF_UNSTABLE, LATEST_VERSION),
diff --git a/engines/wintermute/wintermute.cpp b/engines/wintermute/wintermute.cpp
index e35bb60c3d..955f2dc1c2 100644
--- a/engines/wintermute/wintermute.cpp
+++ b/engines/wintermute/wintermute.cpp
@@ -133,7 +133,7 @@ Common::Error WintermuteEngine::run() {
}
int WintermuteEngine::init() {
- BaseEngine::createInstance(_targetName, _gameDescription->adDesc.gameid, _gameDescription->adDesc.language, _gameDescription->targetExecutable);
+ BaseEngine::createInstance(_targetName, _gameDescription->adDesc.gameId, _gameDescription->adDesc.language, _gameDescription->targetExecutable);
_game = new AdGame(_targetName);
if (!_game) {
return 1;
diff --git a/engines/zvision/configure.engine b/engines/zvision/configure.engine
index 226870c3fd..8681522a35 100644
--- a/engines/zvision/configure.engine
+++ b/engines/zvision/configure.engine
@@ -1,3 +1,3 @@
# This file is included from the main "configure" script
# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps]
-add_engine zvision "Z-Vision" yes "" "" "freetype2 16bit"
+add_engine zvision "Z-Vision" yes "" "" "freetype2 16bit highres"
diff --git a/engines/zvision/detection.cpp b/engines/zvision/detection.cpp
index 5fdf17cd2b..cc967070d9 100644
--- a/engines/zvision/detection.cpp
+++ b/engines/zvision/detection.cpp
@@ -61,7 +61,7 @@ public:
ZVisionMetaEngine() : AdvancedMetaEngine(ZVision::gameDescriptions, sizeof(ZVision::ZVisionGameDescription), ZVision::zVisionGames, ZVision::optionsList) {
_maxScanDepth = 2;
_directoryGlobs = ZVision::directoryGlobs;
- _singleid = "zvision";
+ _singleId = "zvision";
}
virtual const char *getName() const {
@@ -132,7 +132,6 @@ SaveStateList ZVisionMetaEngine::listSaves(const char *target) const {
Common::StringArray filenames;
filenames = saveFileMan->listSavefiles(pattern.c_str());
- Common::sort(filenames.begin(), filenames.end()); // Sort (hopefully ensuring we are sorted numerically..)*/
SaveStateList saveList;
// We only use readSaveGameHeader() here, which doesn't need an engine callback
@@ -155,6 +154,8 @@ SaveStateList ZVisionMetaEngine::listSaves(const char *target) const {
delete zvisionSaveMan;
+ // Sort saves based on slot number.
+ Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator());
return saveList;
}
@@ -164,26 +165,7 @@ int ZVisionMetaEngine::getMaximumSaveSlot() const {
void ZVisionMetaEngine::removeSaveState(const char *target, int slot) const {
Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
- Common::String filename = Common::String::format("%s.%03u", target, slot);
-
- saveFileMan->removeSavefile(filename.c_str());
-
- Common::StringArray filenames;
- Common::String pattern = target;
- pattern += ".###";
- filenames = saveFileMan->listSavefiles(pattern.c_str());
- Common::sort(filenames.begin(), filenames.end()); // Sort (hopefully ensuring we are sorted numerically..)
-
- 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);
-
- // Rename every slot greater than the deleted slot,
- if (slotNum > slot) {
- saveFileMan->renameSavefile(file->c_str(), filename.c_str());
- filename = Common::String::format("%s.%03u", target, ++slot);
- }
- }
+ saveFileMan->removeSavefile(Common::String::format("%s.%03u", target, slot));
}
SaveStateDescriptor ZVisionMetaEngine::querySaveMetaInfos(const char *target, int slot) const {